Looking for an easier way to add a Flash file to a SharePoint content page? Download and install the Flash WebPart SharePoint Solution and you will be able to add Flash movies to your SharePoint web pages without having to edit any html code. Simply add the Flash WebPart to a page, configure it in the WebPart editor by setting the movie URL, width, and height, and you are good to go.

Download the Flash WebPart [MikeKnowles.FlashWebPart.zip]

SharePoint 2007 Installation

1. Unzip FlashWebPart.MikeKnowles.zip

2. Follow the instructions in Readme.txt to install the SharePoint solution to your SharePoint farm.

3. The installation will prompt you to optionally install the Flash WebPart feature to one or more Site Collections. It's highly recommended that you deploy to the Site Collections during the install process as its quicker then having to activate the feature in each site individually.

SharePoint 2010 Installation

Click here for SharePoint 2010 installation instructions.

Using the Flash WebPart

1. If necessary, activate the Feature in the target Site Collection. If you installed the Feature to the Site Collection when you ran the setup program above then you can skip this step.

2. Within the target Site Collection, open a SharePoint page which contains a WebPart Zone and select:
    Site Actions > Edit Page

3. Click to Add a Web Part, and select the SharePoint Flash WebPart as shown below:

image

4. Click Edit > Modify Shared Part

image

5. Within the WebPart Editor, scroll down and click to open the Miscellaneous tab:

image

6. Enter the Flash URL, width, and height of your Flash Movie (SWF). Here are some test values to get you started:

Flash URL: http://kb2.adobe.com/cps/155/tn_15507/images/flashplayerversion1.swf
Flash Width: 280
Flash Height: 149

Bookmark and Share  Comments [12] | Permalink | SharePoint
 

This article describes how to develop a single EXE file for installation and deployment of a SharePoint solution to a Windows SharePoint Services or SharePoint Server farm. The single EXE file can also be used for installation repair and removal. Using the approach outlined in this article can simplify the distribution and installation of your SharePoint code – no more archives to extract, lengthy manual SharePoint admin screens to document and navigate or scripts to run – just a single EXE file to download and execute.

Note: Distribution of an application using the techniques described in this article requires the purchase of WinZip Pro and WinZip Self-Extractor. As of 10/25/09 these products retailed for $49.95 each on winzip.com.

Develop and Deploy Using SharePoint Solution Packages

The most important step and the one that takes the longest to master is to develop all code and assets to be deployed to SharePoint as one or more Features deployed as a SharePoint Solution Package (*.wsp). Much has been written online and in books on how to develop SharePoint Features and Solutions. If you are new to these topics or in need of a refresher, check out the oft-referenced Ted Pattison Office Space columns:

Features for SharePoint

Solution Deployment with SharePoint 2007

Download and Install the CodePlex SharePoint Solution Installer

The SharePoint Solution Installer is a free download from CodePlex licensed under the Microsoft Permissive License (Ms-PL) meaning you may use it to build and distribute commercial software without having to purchase a license or pay royalties (disclaimer: I am not a lawyer, you should verify the applicability of the Ms-PL to your project by consulting a legal professional).

Download and Install WinZip Pro, Self-Extractor, and the Command Line Add-on

Many commercial software products make use of WinZip to deliver and install their software. Our approach is to use WinZip to package up the multiple files required to execute the CodePlex SharePoint Solution Installer. You may download and install the trial versions of WinZip Pro and Self-Extractor to complete the demo, however you will need to purchase licenses prior to deploying software with WinZip.

WinZip Pro Download

WinZip Self-Extractor Download

WinZip Command-Line Add-on Download

Create a Build Directory

The build directory is where we will package up all the files required to build the single installation EXE. This will include the SharePoint Solution Package, application icon, license agreement, the SharePoint Solution Installer Setup.exe and configuration file, and various text files for the WinZip installer. You can learn more about the SharePoint Solution Installer configuration file on the product download site. The WinZip installer options are detailed in the product help file under the topic Command Line Options. For a complete, working example download and extract http://mikeknowles.com/download/SharePointSingleFileSetup.zip and navigate to the Build folder:

 

image 

Create a Build Script

Open the build.bat included in the example download. In line 4, the build script zips up the files required by the SharePoint Solution Installer. Line 6 packages several files for display in the WinZip-based installer and then directs WinZip to execute the SharePoint Solution Installer Setup.exe after extracting all the files. The end result will be an executable with the same name as the zip file (Demo.exe) which is then copied to the parent directory in line 7. The Demo.exe file now contains all files required for a professional-quality installation.

@echo off
del Demo.zip
del ..\Demo.exe
"c:\program files\winzip\wzzip" -o Demo.zip DemoSolution.wsp EULA.rtf Setup.exe Setup.exe.config application.ico
If ErrorLevel 1 Goto Exit
"C:\Program Files\WinZip Self-Extractor\WZIPSE32.EXE" -o Demo.zip -t WinZipMainDialog.txt -i application.ico -a AboutDialogExtra.txt -setup -c Setup.exe
move /Y Demo.exe ../
Goto Exit
:Exit
pause;

Run the Installation Program

The setup file (Demo.exe) must be run while logged into one of the SharePoint Farm servers as a user with Central Administration rights. A common approach is to maintain one account which all SharePoint processes run-as and then perform all SharePoint installations logged into the server as that user. If you try to run the setup program on a Windows XP or Vista computer or a server without SharePoint installed, the program will crash as the SharePoint Solution Installer is unable to initialize. After the program has been installed you can run the Demo.exe program again at which time after running through it’s status checks it will prompt you to repair or remove the installation.

 

image 

image

image

image

image

image

image

image

 

Going Beyond the Basics

You may choose to include a short Readme.txt file in a zip file with the executable as the download for your customers. This provides users with a local file reference for the installation as well as any other useful information in the Readme.txt file such as your website and additional support links and contacts.

Bookmark and Share  Comments [0] | Permalink | SharePoint
 

ASP.NET Profile Properties provide a convenient way to store application and user-specific preferences and personalization choices without having to write a lot of code to manage the back-end data storage and retrieval. The ASP.NET Application Services Database SqlProfileProvider can be leveraged to store and retrieve the Profile settings in a SQL Express or SQL Server database. The SqlProfileProvider will manage the storage of user-specific settings by login name for both forms-based and Windows authentication applications (anonymous user data can be stored in cookies if enabled).

SharePoint 2007 provides an extensive personalization model with it’s own set of management user interfaces and APIs. If your SharePoint site is based on Windows Authentication then SharePoint User Profile Properties are available for storing user-specific data provided personalization has been enabled for the farm and the personalization items have been setup in SharePoint and/or Active Directory.

In certain circumstances functionality being developed for deployment to SharePoint may still need to make use of ASP.NET SQL Server profiles instead of SharePoint user profiles. Some examples of when this might occur:

  1. SharePoint personalization has not yet been configured and enabled or an organization has decided not to enable the personalization system and only a few user-specific properties need to stored.
  2. Common code will be developed that needs to store user-specific preferences in both ASP.NET and SharePoint web applications and the code needs to support both forms-based and Windows authentication.

Recently I found myself in situation #2 and planned on using the ASP.NET SqlProfileProvider to save user preferences within common code that would be deployed to multiple ASP.NET web applications, one of which runs within a SharePoint site collection. After settings things up in Web.config and multiple rounds of writing prototype code, googling, and debugging I was unable to get anything working. No matter what I did, the Profile object was not available and it seemed as if I had hit a dead end. However, thanks to this MSDN forum post I was able to track down why things were not working. By default, SharePoint removes the Profile module which is normally available to an ASP.NET web application and you have to manually add it back in to the Web.config to be able to get and set ASP.NET Profile properties.

Web.config Profile (add to system.web)

<profile enabled="true" automaticSaveEnabled="true" 
         defaultProvider="AspNetSqlProfileProvider">
    <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider"
             connectionStringName="YOUR_CONNECTION_STRING_NAME"
             applicationName="YOUR_APPLICATION_NAME"
             type="System.Web.Profile.SqlProfileProvider, 
                 System.Web, Version=2.0.0.0, 
                 Culture=neutral, 
                 PublicKeyToken=b03f5f7f11d50a3a" />
    </providers>
    <properties>
        <add name="Property1" type="System.String" />
        <add name="Property2" type="System.String" />
        <add name="Property3" type="System.String" />
    </properties>
</profile> 

Web.config Module Include (add to httpModules)

<add name="Profile" type="System.Web.Profile.ProfileModule" />

Getting and Setting the Profile Properties in SharePoint Code

As explained in this post by Becky Bertram in SharePoint the Profile attributes defined in Web.config are not available at compile-time to SharePoint code. In order to get code to compile for deployment to SharePoint as a solution you have 2 choices:

1. Write a custom provider which maps to each attribute (Becky’s article provides an example of this for forms-based authentication which will also work for Windows authentication).

2. Make calls to Profile.GetPropertyValue and Profile.SetPropertyValue to get and set the profile values for the current user.

For example, given the above Web.config definitions, the code below would set values for the current user that would be saved to the ASP.NET SQL Profile database:

Profile.SetPropertyValue("Property1", "blue");
Profile.SetPropertyValue("Property2", System.DateTime.Now.ToString()); 

Prototype Code

I’ve found an effective way to work through “it should work but I better make sure before I design a system around it” issues like this in SharePoint is to write up prototype code that is simply deployed manually to the _layouts folder. Then I just open the ASPX/CS files in Visual Studio, make changes, and reload the page in the browser the same as if I was developing for ASP.NET. To help get you going with setting up the Profile database I’ve included my Windows authentication and ASP.NET Profile prototype code below which you can also download as a zip file (see the zip file readme for steps to deploy on your SharePoint development server). Here’s an example of the prototype code output followed by the code listings:

image

WindowsAuthenticationContext.aspx

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="WindowsAuthenticationContext.aspx.cs" 
    Inherits="WindowsAuthenticationContext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Windows Authentication Context</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <pre><asp:Literal ID="IdentityInfoLiteral" runat="server" />
            </pre>
        </div>        
        <div>
            <asp:Literal ID="ExceptionLiteral" runat="server" />
            <asp:Literal ID="ExceptionStackTraceLiteral" runat="server" />
        </div>        
    </form>
</body>
</html>

WindowsAuthenticationContext.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.IO;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.Profile;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls; 

public partial class WindowsAuthenticationContext : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            StringWriter writer = new StringWriter(); 

            writer.WriteLine("Context.User.Identity.AuthenticationType=" + 
                Context.User.Identity.AuthenticationType);
            writer.WriteLine("Context.User.Identity.IsAuthenticated=" + 
                Context.User.Identity.IsAuthenticated);
            writer.WriteLine("Context.User.Identity.Name=" + 
                Context.User.Identity.Name);
            writer.WriteLine(); 

            WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
            writer.WriteLine("WindowsIdentity.GetCurrent().AuthenticationType=" + 
                windowsIdentity.AuthenticationType);
            writer.WriteLine("WindowsIdentity.GetCurrent().IsAuthenticated=" +
                windowsIdentity.IsAuthenticated);
            writer.WriteLine("WindowsIdentity.GetCurrent().Name=" +
                windowsIdentity.Name);
            writer.WriteLine("WindowsIdentity.GetCurrent().User=" +
                windowsIdentity.User.ToString());

            if (Context.Profile != null)
            {
                writer.WriteLine();
                writer.WriteLine("Profile.IsAnonymous=" +
                    Profile.IsAnonymous);
                writer.WriteLine("Profile.UserName=" +
                    Profile.UserName);
                writer.WriteLine("Profile.LastActivityDate=" +
                    Profile.LastActivityDate.ToLongDateString());
                writer.WriteLine("Profile.LastUpdatedDate=" +
                    Profile.LastUpdatedDate.ToLongDateString());
                writer.WriteLine("Profile.GetType=" +
                    Profile.GetType());
                foreach (SettingsProperty property in 
                    ProfileBase.Properties)
                {
                    writer.WriteLine(">>Property name=" +
                        property.Name + " value=" +
                        Profile.GetPropertyValue(property.Name));
                }                

                writer.WriteLine();                
            }        
            IdentityInfoLiteral.Text = writer.ToString();           
        }
        catch (Exception ex)
        {
            ExceptionLiteral.Text = ex.ToString();
            ExceptionStackTraceLiteral.Text = ex.StackTrace.ToString();
        }
    }
}
Bookmark and Share  Comments [1] | Permalink | SharePoint
 

Here’s a quick way to get a listing of the currently running SharePoint application pool process IDs. Save this script in the directory of your choice and name it with a .bat extension (e.g. GetAppPoolProcessIds.bat):

cscript.exe %systemroot%\system32\iisapp.vbs
pause

Double-click the script and it will display the list of processes. Click any key to close the window:

image 

Add the script to the Windows Start Menu and click it each time you need to see the process list. This is what I do each time I run the Visual Studio Debugger to debug a DLL deployed to a SharePoint application.

image

Bookmark and Share  Comments [0] | Permalink | SharePoint
 

Recently I’ve been asked how to get Google Analytics working in an intranet site with a single name in the URL, for example: http://intranet. The first time I added Google Analytics to a site it was a development site accessed by server-name:port-number. For days after correctly adding the tracking code there was still no data showing up in the Google Analytics dashboard reports.

Turns out the fix required adding one simple JavaScript call to a Google JavaScript API function. When adding Google Analytics tracking code to a site with a single name in the URL you need to call _setDomainName(“none”) prior to making the call to _trackPageView:

image

By default the Google Tracking Code snippet provided in the Google Analytics Profile Settings uses the “auto” domain name mode which expects to see a fully-qualified domain name (FQDN) composed of the domain name, followed by a period, followed by the domain extension (.com, .net, .biz, etc.). Setting the domain name to “none” disables the FQDN requirement and will record your pages in Google Analytics the same as a site with a FQDN.

I have deployed this fix with success to both a SharePoint 2007 Team Site and Publishing Portal site available only by a single name URL and port number. In both cases the site data was not showing up in Analytics until I added the call to set the domain name to “none”. It’s critical that you call _setDomainName before calling _trackPageView for this change to have any effect.

As with all changes to JavaScript tracking code it can take up to 24 hours before you see data start showing up in the Google Analytics dashboard reports, and changes only apply going forward so there’s no way to recover clicks from before you deployed the code change.

Here’s an example of the tracking code with the domain name set to “none”. Replace YOUR_WEB_PROPERTY_ID with the value assigned to your site profile by Google Analytics:

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + 
    "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("YOUR_WEB_PROPERTY_ID");
pageTracker._setDomainName("none");
pageTracker._trackPageview();
} catch(err) {}</script>
Bookmark and Share  Comments [6] | Permalink | GoogleAnalytics | SharePoint
 

This article provides a template for adding the breadcrumb to the top of your custom settings pages. If you are unfamiliar with the steps involved to create a custom settings page and have it show up in the links in the Site Collection Administrators section on the Site Settings page, first check out these posts:

Using the SPPropertyBag with Custom Admin Pages in SharePoint by Steve Gragert

Office Space: Custom Auditing In SharePoint by Ted Pattison

Using the techniques described in the above-mentioned articles I was able to setup a custom settings page and access it from the Site Settings page:

SiteCollectionAdminLink

However once loaded I noticed there was no breadcrumb displayed above the new page title:

SiteSettingsNoBreadcrumb

The reason the breadcrumb does not appear is because SharePoint does not have the custom settings page in it’s sitemap file which exists on disk “somewhere” in the 12 hive. Furthermore, SharePoint does not provide a programmatic means to add the page to the sitemap by updating a feature configuration or writing code that uses the object model. The only way to add your page to the sitemap is to physically update the sitemap file on every server in the farm. And as every good SharePoint student learns early on, we should not modify files on disk because they might change in future upgrades and it makes farm deployment across multiple servers more time-consuming and error prone due to the need to update every server manually.

After a couple of days working on this admin page it was quite aggravating not having the breadcrumb since that is what I always use to get back to the main Site Settings page. So here’s what I did: using Firebug I found the html generated by admin.master when it builds the breadcrumb and then added that to my page by overriding the content section populated in the master page. Since the custom settings page will always be deployed at the Site Collection level, the only item I had to populate dynamically was the Site Collection title. The custom settings page title can simply be added in the ASPX for the page:

SiteSettingsBreadcrumb

Here’s the code template to add to your custom settings ASPX page. Substitute “SETTINGS PAGE TITLE” below with your custom settings page title:



    
    
        
     > 
    
        
    
     > 
    SETTINGS PAGE TITLE



Add the variable and text assignment for SiteRootLink to the code-behind for your custom settings page as shown below. The code snippet assumes the custom page inherits from Microsoft.SharePoint.WebControls.LayoutsPageBase:

protected HyperLink SiteRootLink;

protected void Page_Load(object sender, EventArgs e)
{
    this.SiteRootLink.Text = this.Site.RootWeb.Title;

Admittedly this is not the most robust of solutions because it’s possible Microsoft may change the style definitions or controls used to generate the PlaceholderTitleBreadcrumb. I do think it’s one step better then having to modify the sitemap file on all the servers in a farm due to the inconvenience and the equally likely chance a future upgrade might change the location or structure of the sitemap file. So it’s not ideal, but at least it’s something that can be packaged into a feature and deployed without the need for manual changes after feature deployment.

Bookmark and Share  Comments [0] | Permalink | SharePoint
 

Have you ever started out developing a class library for web parts or utility classes only to realize you later need to add a User Control or Page to the project? This can happen when working with SharePoint solutions as a popular way to develop and package solutions is as a class library project (DLL).

However, you can also package SharePoint solution DLLs by building a Web Application project file, which produces a single DLL which can then be deployed to the GAC or web application as part of a solution package (veterans of ASP.NET 1.x you will recognize web application projects as the way “it used to work” when developing web applications prior to ASP.NET 2.0). You just have to remember that it’s not a true web application project when deployed to SharePoint, because SharePoint “owns” the web application and if you add a Global.asax or Web.config to the project and then deploy as a solution, it won’t have any effect since SharePoint sees your code as a DLL (refer to this post for details of how to create an event handler in lieu of a Global.asax function). However, developing as a web application project in Visual Studio means you can work on User Controls and Pages which are then deployed in the solution as CONTROLTEMPLATES and LAYOUTS.

So if you have found yourself in the situation where you started out with a class library project and need to convert to a web application project, it’s an easy change to make. This change works even if your project has nothing to do with SharePoint.

Open the .csproj file in a text editor, and find the definition for ProjectGuid – it’s probably on or around line 7 (ignore the GUID below, that’s going to be specific to your .csproj file)

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.50727</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{9A693F59-9C63-4424-A98D-D69A801C7614}</ProjectGuid>    

Paste this line after the <ProjectGuid> line:

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Leave all other properties unchanged. Your file should now look something like this:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.50727</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{9A693F59-9C63-4424-A98D-D69A801C7614}</ProjectGuid>    
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>        
    <OutputType>Library</OutputType>

Save the .csproj file and open in Visual Studio 2008. You should now be able to add Pages and User Controls to your project yet still compile to a single DLL suitable for deployment as part of a SharePoint solution.

Bookmark and Share  Comments [2] | Permalink | ASP.NET | SharePoint
 

Google Analytics is a web traffic and demographics reporting service provided free of charge by Google. Google Analytics functionality can be added to any web site by setting up an account and adding a snippet of JavaScript to every page within the web site. Once configured you can see all types of statistics such as how users were referred to your site, number of hits per page, where your users are located geographically, what types of browsers and OS they are using, and much more.

This post outlines how to add Google Analytics to a SharePoint Publishing site. You must have edit rights for all site Master Pages. Google Analytics can complement the Usage Reports available to Site Collection Administrators and Search Term Reports available to Shared Service Provider Administrators. Analytics can also be used to provide read-only access to users who do not have administrative rights on the SharePoint site but whose job function might benefit from access to detailed site usage data.

1. Setup a Google Analytics Account for the Site

1-1. Click here to register your site for Google Analytics. Note that the web site you are registering can be a public, intranet, or private company site. You can register the site with a new or existing Google account, and you have the option at any time to allow other Google accounts to act as administrator or have read-only rights to the Analytics reports.

1-2.  After you have registered and created a profile for your site, get the JavaScript code for the site. These 2 help pages on Google explain how to find the JavaScript code and add it to your site pages (don’t add any JavaScript to your site pages yet):

Where can I find my tracking code from within my Google Analytics account?

How do I add tracking code to my website?

TrackingCode

1-3. Open your site in a browser, and evaluate the page titles displayed in the browser title bar and tabs as you click through the site. Do your pages have meaningful page titles that are unique for each page? If not, you should correct this before adding Google Analytics to your site. Otherwise you will not be able to differentiate the traffic for each page.

UniquePageTitles

2. Configure Google Analytics to Report on Search Terms Entered by Users

Google Analytics provides a quick and easy way to track the search terms being entered by users of your SharePoint Publishing site. Although SharePoint can be configured to track search terms, it requires additional configuration by the Farm Administrator and the search terms are only visible to the Shared Service Provider Administrators. To fully take advantage of SharePoint search functionality such as Best Bets, Site Collection Administrators and Content Authors need to know what search terms are being entered by users. Having to manually request the Shared Service Provider deliver this information on an ongoing basis is a waste of resources when you can easily configure Google Analytics to track it at no expense.

Assuming you are using a SharePoint search site and have not altered any of the defaults or code for search queries, all SharePoint search term queries are submitted using the parameter name k. It’s important that you are not using the parameter name k for any of your forms or custom development running within the site. To configure Google Analytics to track the search term queries, you need to configure the Site Search settings in the Google Analytics site profile. See the Google Analytics Help article How do I set up Site Search for my profile? for details of how to find and configure the Site Search settings.

After you have setup Site Search, your settings should look like the screenshot below:

image

3. Add Tracking Code to the Site Master Pages

The next step is to add the JavaScript tracking code obtained in step 1-2 to your site Master Pages. As described in the Google references in section 1, you need to add the tracking code to the end of each site Master page, just prior to the </body> tag. How you do this depends on which of the 3 cases below your site development and publishing processes fall into:

3-1. If the site is production only, meaning you don’t have a development, test, or staging environment, and the changes you make to the Master Pages are immediately applied to production, then just paste the JavaScript into the master pages prior to the </body> tag, save, and publish.

3-2. If the site has pre-production environments such as development, test, or staging, and site Master Pages are maintained as content specific to each environment and solution files or content deployment are not used to publish the same Master Page to each environment, then you can still copy-paste tracking code to the production environment only.

3-3a. If the site has pre-production environments such as development, test, or stage, and the same version of each Master Page is published to each environment with solution files or content deployment, then you will want to setup a mechanism to prevent having your pre-production traffic logged as production traffic. Otherwise the clicks generated in your pre-production environments by developers and testers will show up in the reports and bias the interpretations of site traffic, what pages are most popular, etc.

If your site falls into case 3-3, you have 2 choices for filtering out the pre-production clicks from your Google Analytics reports. If you know the IP addresses or range of IP addresses for all users who will access the pre-production sites, and you don’t need to track those users when they access your production site, then you can exclude specific IP addresses or a range of IP addresses in the Analytics Settings. If you cannot use IP filtering for all pre-production users, or if you need to capture the IP addresses of those users in production, then you can add code to your Master Pages or create an ASP.NET User Control and include it in your Master Pages to conditionally render the tracking code.

3-3b. Create a User Control to Conditionally Render the Tracking Code

If your situation falls into category 3-1 or 3-2 above, then you can skip this section. If your site falls into case 3-3 and you need to conditionally include or exclude the JavaScript tracking code depending which environment you are running in, then you can achieve this in a reusable fashion with an ASP.NET User Control.

3-3-1. Add an AppSetting configuration property to Web.config or any config file readable by the ASP.NET ConfigurationManager which sets a flag to display or hide the tracking code in the environment in which the code is running. This flag would be set to true only in the production environment, or in pre-production environments when testing that the code is included correctly.

<add key="IncludeGoogleAnalytics" value="false" />

3-3-2. Create a user control called GoogleAnalytics.ascx and deploy to _controlTemplates. The code below does not display the tracking code when the flag from step 3-3-1 is set to false or the current page is in edit mode. This is to prevent tracking of these requests and also to limit the possibility of JavaScript clashes when editing publishing pages:

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeBehind="GoogleAnalytics.ascx.cs" 
    Inherits="GoogleAnalytics" %>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + 
    "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("YOUR_SITE_TRACKER_ID");
pageTracker._trackPageview();
} catch(err) {}</script>

public partial class GoogleAnalytics : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // FormMode publishing states: Display, Edit, Invalid, New
        this.Visible = ConfigurationManager.AppSettings["IncludeGoogleAnalytics"] == "true" &&
            (SPContext.Current.FormContext.FormMode != SPControlMode.Edit &&
             SPContext.Current.FormContext.FormMode != SPControlMode.New);
    }
}

3-3-3. Include the new user control at the end of each Master Page.

<%@ Register TagPrefix="Google" TagName="GoogleAnalytics" 
    src="~/_controltemplates/YOURPATH/GoogleAnalytics.ascx" %>
    <Google:GoogleAnalytics Visible="false" runat="server" />    
</body>
</html>

4. Using Google Analytics

After your have published the Google Analytics tracking code and confirmed all pages in your production environment are including the JavaScript tracking code, then you can start to track your site reports in Google Analytics. Analytics currently updates once each night for the previous day’s traffic, and after you deploy the code for the first time it will take 12-24 hours for data to start showing up in the Analytics Dashboard.

In addition to the Dashboard displayed when you click to view the Site Reports in Google Analytics, I have found the 4 screens listed below to be very useful in understanding how users are accessing the site. The navigation path required to reach each screen are pictured as a reference for how to access these reports.

4-1. Content by Title


image

4-2. Map Overlay


image

4-3. Site Search Terms

image

4-4. Browsers and OS

image

Bookmark and Share  Comments [34] | Permalink | GoogleAnalytics | SharePoint
 

An easy way to modernize your website is to add a favicon - which is the small postage stamp-like image displayed in Firefox and IE7 next to the address bar and the tab name.

favicon1

favicon2

Adding a favicon to your website is easy and doesn't require modifying any html or code files:

1. Go to www.favicon.cc and download an existing favicon or create your own. Download the icon and save it locally as favicon.ico

2. Upload favicon.ico to the root of your website.

Firefox and IE7 will automatically display your favicon if it's in the icon image format, named favicon.ico, and available in the root of your website.

With SharePoint, one option is to put the favicon.ico in the root directory of your site collection's IIS virtual directory. However if you want to deploy the favicon.ico with a solution to either the content database or the _layouts folder, then you can point the browser to your favicon with a link tag in the head section of your page or master page:

<link rel="shortcut icon" href="/_layouts/MyLayoutFolder/Images/favicon.ico"> 
Bookmark and Share  Comments [0] | Permalink | SharePoint | WebDesign
 

Here's a simple yet useful method for checking membership in a SharePoint group. This method will return true or false to indicate if the current user is a member of the specified SharePoint group in the current site. The current user is obtained from an assumed impersonation context.

public static bool IsUserInGroup(string groupName)
{
    bool inGroup = false;

    try
    {
        SPGroup group = SPContext.Current.Web.Groups[groupName];               
        inGroup = group.ContainsCurrentUser;
    }
    catch (Exception ex)
    {
        // SharePoint throws an exception if the group indexer
        // is not a group the current user belongs to.   
        inGroup = false;
    }
    return inGroup;
}
Bookmark and Share  Comments [2] | Permalink | SharePoint
 

When developing with SharePoint it can be frustrating when changes are deployed and activated in a Solution package and the changes do not appear in the browser. This can include CSS, image, and JavaScript files and also Master Page and Page Layout changes. This post outlines some of the mechanisms to be aware of when planning your solution update strategies and troubleshooting "missing" content changes.

Where should I deploy CSS and Image files?

The decision where to deploy "visual" files that are cacheable in the browser such as CSS, image, and JavaScript files depends on who will own the content once the system is live. If the content needs to be owned and maintained by the content administrators, then deploy it to the content database (somewhere under Style Library). However be aware that once your content administrators modify the content, future deployments of your solution will have no impact on the file in the content database - this is true even if you retract and re-deploy your solution (although there is a workaround to reset to the site definition described below). If the content needs to be changed in the future by the development team, then deploy the files to a solution-specific folder under the 12 hive LAYOUTS folder.

Where should I deploy Publishing Master Pages and Page Layouts?

Master Pages and Page Layouts must be deployed to the content database. This opens up the possibility that your content administrators can modify these files. It's also common during development for a creative or development resource to work with the Master and Page Layouts in SharePoint Designer and then deploy them later with a solution. If a Master Page or Page Layout is modified from the content database, then future updates to that file when you re-deploy your solution will have no effect. You will be left wondering, I know I deleted and re-installed the solution, why aren't my changes showing up? You can clear your browser cache several times, and restart SharePoint, and still the changes will not show up.

The reason for this is as with images and CSS files deployed to the content database, once Master Pages and Page Layouts have been customized then SharePoint does not allow solutions to overwrite them. Depending who you talk to or what article you read, or how late you end up working when vexed by this problem, you might consider this a feature or a bug. However SharePoint does provide a way to reset the content database to the version you have deployed, with the caveat that all history is erased. The way to do this is go into Site Settings > Modify All Site Settings and under the Look and Feel column click Reset to site definition:

reset

Next enter the complete path to the file you want to reset. Click here for more of the theory and details on resetting customization changes. This technique works for any file deployed to the content database - image, CSS, JavaScript, Master Page, Page Layout, etc. However it also erases all version history, so please exercise this power responsibly.

SharePoint Caching

SharePoint provides a caching architecture which helps to minimize the overhead of retrieving dynamic content from the databases it manages and to provide other performance improvements. Click here for a useful overview of the caching architecture. You can manage and clear the caches in Site Settings > All Site Settings by clicking the indicated links below:

sca

SharePoint IIS Content Expiration Settings

Recall from earlier my recommendation that CSS, image, and JavaScript files that will be deployed as part of solution and need to be updated by developers should be deployed to a solution-specific layouts folder under the 12 hive. This prevents the files from becoming customized. A second benefit is that by deploying to the file system under the layouts folder, you can now set caching directives in IIS to tell browsers how frequently to check for new versions of the content.

Here's something you need to know that you won't see frequently documented but which can have enormous impact on your changes not making it down to the browser: By default SharePoint sets a far futures header of 365 days on all files deployed to the LAYOUTS and CONTROLTEMPLATES folders under the 12 hive. This means that if you deploy an image, CSS, JavaScript, or ASPX application page to any of these folders, the browser will not check back for 1 year to see if the content has changed. Each user would have to clear their cache or explicitly reload the page to see the changes. Note that this default is very different then what we ASP.NET developers are used to - normally when you create a site in IIS there are no far futures headers set.

Fortunately there is an easy way to prevent your content from being cached and then not being reloaded when you later deploy a new version of your solution. You can turn off content expiration for your solution-specific folder under the LAYOUTS folder, and likewise for CONTROLTEMPLATES if you are deploying any browser-loadable files there (ASCX files are not loaded by the browser).

Right-click the solution-specific folder under _layouts and click the Http Headers tab. Note that content is set to not expire for 365 days by default:

mct

Uncheck "Enable content expiration" and Apply the changes:

sca2

Note that you and your users will need to clear their browser cache to reload the content the first time. After that the content will be refreshed when changed in the web browser. This is why it's so important if possible to plan this out before your site is launched - otherwise if you have deployed browser-loadable files then the browsers are all sitting there thinking the content doesn't expire for 365 days after they first accessed your site. If the situation you are in is your site has already been launched, then changing the names of the files and updating your links is one way you can reset the clock.

Finally, a great tool to help in resolving caching and content updates is the freely available Fiddler Web Debugger, an easy to install proxy which can be used to trace all HTTP and HTTPS traffic. The Firefox Firebug and YSlow add-ons are useful in helping to track down how many files are being loaded and what the key headers (like Expires) are set to.

Bookmark and Share  Comments [3] | Permalink | SharePoint
 

This post describes how to configure a SharePoint application so that you can write to log4net from within custom code you are developing and deploying to the web application. When working with log4net in SharePoint or ASP.NET code that is running in Windows Authentication mode with impersonation enabled, you need to configure and write to log4net using the process execution context and not the context of the user accessing the web application. The reason is that unless the user accessing the site has the rights to read the log4net configuration and write to the disk on the server then you will find that your log is either not created or has no or seemingly random output.

Updated 2/23/2010: I've added an example download with complete source code and a sample web.config file for a SharePoint solution that deploys a web part and http module. The module is setup to perform the log4net initialization as described below. The web part displays a text button and logs whatever you type to the log4net logfile. If you run the setup.exe included in the zip it will install the solution to whatever site(s) you choose. Then you can add the web part and use that to generate test writes to your log file. However the logging won't work until you have performed the steps outlined below.

Click here to download the example and complete source code

The first step is to deploy the log4net DLL to the GAC (the example download does this automatically). Next step is to specify the log4net configuration. There are many options and examples documented on the log4net web site. For this example the log4net configuration will reside in Web.config and will write to a RollingFileAppender. First, add an entry to the Web.config configSections to register the new configuration block:

Next add the log4net configuration section to Web.config just prior to the system.web section. Note that in the configuration below the file is set to reside in App_Data\Logs. You will need to create these directories in the webroot of each SharePoint web application which needs to use log4net if they do not already exist. (An example of a webroot is the following for a site running on port 40062: C:\Inetpub\wwwroot\wss\VirtualDirectories\40062).


  
    
    
    
    
    
    
    
      
    
    
      
    
  
  
    
    
  

Within your custom code there needs to be an explicit API call so the first-time initialization will occur. Although log4net does provide an auto-watch capability to look for configuration settings in Web.config, I have found this alone does not work when your application is executing in Windows Authentication mode with impersonation enabled. The workaround is to explicitly initialize log4net by creating an HttpModule initialization method and calling the log4net XmlConfigurator.Configure() method. Here's an example module - note that you can just as easily add the required code to an HttpModule you may already have setup:

using System;
using System.Text;
using System.Web;

using log4net;
using log4net.Config;
using log4net.Util;

[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace MikeKnowles
{
    public class InitLog4NetModule : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            XmlConfigurator.Configure();
        }

        public void Dispose() { }
    }
}

HttpModules are registered in the Web.config httpModules section. The entry needs to point the ASP.NET runtime to the DLL which contains your module code.

    
 

After the log is configured and all assemblies deployed the application can write to log4net. The example web part source code included in the download just writes whatever text is entered by the user to the log at DEBUG scope (see goButton_click method below).

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Utilities;

using log4net;

namespace MikeKnowles
{
    public class Log4netExampleWebPart : System.Web.UI.WebControls.WebParts.WebPart
    {
        Button goButton;
        TextBox logTextBox;

        public Log4netExampleWebPart()
        {
        }

        protected override void CreateChildControls()
        {
            // Create text box
            logTextBox = new TextBox();
            logTextBox.Width = 360;
            logTextBox.Text = "Enter log text";
            this.Controls.Add(logTextBox);

            // Create button
            goButton = new Button();
            goButton.Text = "Go";
            goButton.Click += new EventHandler(goButton_click);
            this.Controls.Add(goButton);
        }

        public void goButton_click(object sender, EventArgs e)
        {
            ILog log = LogManager.GetLogger("ExampleLogger");
            log.Debug(logTextBox.Text);
        }
    }
}
Bookmark and Share  Comments [0] | Permalink | log4net | SharePoint
 

Click below to download the full source code from my presentation Saturday on developing custom editors for SharePoint WebParts.

Download Source Code (107 KB zip file)

Reference Articles:

Working with ASP.NET 2.0 Web Parts and Windows SharePoint Services 3.0

Plan for personalized Web Parts

Bookmark and Share  Comments [2] | Permalink | SharePoint
 

When working within a SharePoint Publishing site, content editors can enter html using the WYSIWIG editor within a PublishingPageContent Field Control or a Content Editor Web Part. When a site collection is created from the Publishing Portal site definition, the field control and web part are defined with slightly different styles. By merging the style definitions together, you can achieve a unified look in both the field control and web part content entered by your content administrators. This post also describes how you can extend the style definitions so that the WYSIWIG editor applies single-line spacing instead of double-line spacing when an editor hits the carriage-return (Enter) key.

The first step is to create and configure the Alternate CSS URL associated with your Publishing site Master Page. You can either create and edit the style sheet in SharePoint Designer, upload a style sheet and then set it as the Alternate CSS URL in Site Settings, or create and deploy the style sheet as part of a Feature package and then execute code in a feature receiver or batch job to set the style sheet. Here's how it looks if you set the property in Site Settings > All Site Settings > Master Page after having uploaded the CSS file to the Style Library:

image

If you prefer to code it, the following pseudo-code will set the style sheet named mystyles.css to be the alternate style sheet for the Publishing Site Collection running at http://localhost:8087:

using (SPSite site = new SPSite("http://localhost:8087"))
{
    using (SPWeb rootWeb = site.RootWeb)
    {
        PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(rootWeb);
        publishingWeb.AlternateCssUrl.SetValue("/Style Library/mystyles.css", true);
        publishingWeb.Update();
        rootWeb.Update();      
    }
}

The alternate style sheet is loaded last in the chain of style sheets on every page in a Publishing site that is displayed within the site master page. You can see this in action by doing a view-source on the page and looking at the CSS includes. The IE Internet Developer Toolbar and Firefox FireBug add-on are also helpful for seeing the styles applied during the cascade.

When creating your own custom styles for the way content is edited and rendered, the important styles to override are pageContent for the field control and ms-WPBody for the web part. Combine these definitions together in your alternate style sheet and define content tags as you would for a "regular" html page. For example, this definition will render all text in either the field control or web part using the same shared font styles (Medium Height Charcoal gray, no serifs):

.pageContent, .ms-WPBody
{
    font-family: Verdana;
    font-size: 12px;
    color: #666666;
}

image

As you can see above by the spacing after Heading 2 and Heading 3, the default styles are defined such that the WYSIWIG editor will insert 2 vertical lines (double-spacing) when the editor hits the Enter key. This can make it difficult to achieve precise formatting (a nice way of saying it adds too much white space). Although it's possible to use Shift-Enter to insert a single line, the downside is the same style is applied to the chunk before and the chunk after the Shift-Enter, so this approach is useless with headings. By resetting the margins for text elements displayed within our pageContent and ms-WPBody styles we can provide the user's with single line spacing and more control over vertical placement:

.pageContent, .ms-WPBody h1, h2, h3, h4, h5, h6, p, span, ul, ol, li, br
{
    margin-top: 0in;
    margin-bottom: 0in;
}

image

Bookmark and Share  Comments [0] | Permalink | SharePoint
 

insert_table

(Updated June 7th, 2009)

When editing content a SharePoint Publishing site, you can select from 5 predefined table formats when inserting a table. The image on the left lists the table formats available to select from. Although the table formats are worth consideration, they may not match the unique fonts, colors, and other branding defined elsewhere within your web site.

You have 2 choices when deciding how to integrate your own CSS-based custom table formats: you can replace the predefined formats with your own style definitions, or you can add to the list of predefined formats that ships with SharePoint. Whichever approach you decide to take, both require looking at the predefined formats that ship with SharePoint and using those as a basis for your new style definitions.

Create Your Custom Table Format Style Definitions

To define the new table formats it’s easiest to start by working from the style definitions of the SharePoint predefined formats.  Fire up your favorite CSS editor (I recommend Visual Studio or Notepad++) and open the file:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\1033\STYLES\HtmlEditorTableFormats.css

Read through the file and notice how it's a series of repeating style definitions with a repeated prefix pattern of .ms-rteTable-TABLE_FORMAT_NAME, where TABLE_FORMAT_NAME is the text displayed in the predefined table format list shown above. Currently the TABLE_FORMAT_NAMES are all integers. However you are not restricted to using just numbers when you create your own formats.

Now that you can see how the styles are defined, create a temporary CSS file and copy-paste the CSS statements for one of the existing table formats into your temporary file, and replace all instances of the string TABLE_FORMAT_NAME with your own table name and save the file. Make sure there are no spaces or special characters in the TABLE_FORMAT_NAME and that all styles have been accounted for.

To get you started, the code below is for a table format I created which starts from predefined table format 1 and redefines the background colors displayed on the table rows:

/* Table Format Aluminum */
.ms-rteTable-Aluminum   {
    border-width: 0px;
    border-style: none;
    border-collapse: collapse;
    font-family: Tahoma;  
}
.ms-rteTable-Aluminum tr.ms-rteTableHeaderRow-Aluminum      {
    margin: 10px;
    padding: 10px;
    color: black;
    background: #babdb6;
    text-align: left;
    font-size: 10pt;  
    font-style: normal;
    font-family: Tahoma;  
    text-transform: capitalize;
    font-weight: bold;
    border-spacing: 10px;
    line-height: 14pt;
    vertical-align: top;
}
.ms-rteTable-Aluminum td.ms-rteTableHeaderFirstCol-Aluminum    {
    padding: 0in 5.4pt 0in 5.4pt;
    color: #3a4663;
    line-height: 14pt;
}
.ms-rteTable-Aluminum td.ms-rteTableHeaderLastCol-Aluminum  {
    padding: 0in 5.4pt 0in 5.4pt; 
    color: #3a4663;
    line-height: 14pt;
}
.ms-rteTable-Aluminum td.ms-rteTableHeaderOddCol-Aluminum {
    padding:0in 5.4pt 0in 5.4pt; 
    color: #3a4663;
    line-height: 14pt;
}
.ms-rteTable-Aluminum td.ms-rteTableHeaderEvenCol-Aluminum 
{
    padding:0in 5.4pt 0in 5.4pt; color: #3a4663;
    line-height: 14pt;
}
.ms-rteTable-Aluminum tr.ms-rteTableOddRow-Aluminum  {
    color: black;
    background-color: #eeeeec;
    font-size: 10pt;   
    vertical-align: top;
}
.ms-rteTable-Aluminum tr.ms-rteTableEvenRow-Aluminum   {
    color: black;
    background-color: #d3d7cf;
    font-size: 10pt;   
    vertical-align: top;
}
.ms-rteTable-Aluminum td.ms-rteTableFirstCol-Aluminum  {
    padding: 0in 5.4pt 0in 5.4pt;
}
.ms-rteTable-Aluminum td.ms-rteTableLastCol-Aluminum {padding:0in 5.4pt 0in 5.4pt;}
.ms-rteTable-Aluminum td.ms-rteTableOddCol-Aluminum  {
    padding: 0in 5.4pt 0in 5.4pt;
}
.ms-rteTable-Aluminum td.ms-rteTableEvenCol-Aluminum {
    padding:0in 5.4pt 0in 5.4pt;
}
.ms-rteTable-Aluminum tr.ms-rteTableFooterRow-Aluminum     {
    background-color: #babdb6;
    color: black;
    font-weight: bold;
    font-size: 10pt;  
    font-family: Tahoma;  
    line-height: 11pt;
}
.ms-rteTable-Aluminum td.ms-rteTableFooterFirstCol-Aluminum  {
    padding: 0in 5.4pt 0in 5.4pt;
    border-top: solid gray 1.0pt;
    text-align: left;
}
.ms-rteTable-Aluminum td.ms-rteTableFooterLastCol-Aluminum {
    padding:0in 5.4pt 0in 5.4pt;
    border-top:solid gray 1.0pt;
    text-align:left;
}
.ms-rteTable-Aluminum td.ms-rteTableFooterOddCol-Aluminum  {
    padding: 0in 5.4pt 0in 5.4pt;
    text-align: left;
    border-top: solid gray 1.0pt;
}
.ms-rteTable-Aluminum td.ms-rteTableFooterEvenCol-Aluminum  {
    padding: 0in 5.4pt 0in 5.4pt;
    text-align: left;
    border-top: solid gray 1.0pt;
}
/* END Table Format Aluminum*/

Approach 1 – Replace the Predefined Formats with Custom Style Definitions

Add your custom style definitions to one of your site CSS files to replace the table definitions provided by SharePoint. For more detail on the inner-workings of this approach, refer to this Microsoft article (scroll down to the sub-heading: Predefined Table Formats).

Approach 2 – Add to the List of Predefined Formats that Ships with SharePoint

To add your styles to the list provided by SharePoint, you will need write access to the file system on the SharePoint server. As with any modification of SharePoint files, you should make backups, store them in source control, and test all changes on a pre-production server before moving into your live environment. Simply paste your style definitions to the end of the file we started from earlier in the article and save the file:

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\1033\STYLES\HtmlEditorTableFormats.css

Create a New Table with Your Custom Style Definitions

Regardless of the approach used, you should now be able to create a new new table in a page and your new table format should appear (you may need to restart IIS and/or flush the cache in your web browser). If you followed Approach 1, your format will be the only item in the list. If you followed Approach 2, your item will appear at the end of list after the SharePoint predefined formats.

Here's how the example definition presented earlier looks in the Predefined Table Format dialog:

format2

Bookmark and Share  Comments [2] | Permalink | SharePoint

One of the development challenges with SharePoint is setting up a generic error handler so that you can display a site-specific error page instead of the error pages displayed by SharePoint. Although SharePoint 2007 is built on top of ASP.NET 2.0/3.5, there are some facilities available in ASP.NET that don't work the same when run through SharePoint because of the way SharePoint handles most of the page-rendering events to facilitate loading dynamic content from the SharePoint database.

Typically in ASP.NET to implement an error handler you can implement a global.asax error handler and log the error and redirect to an error page. Global.asax events don't work the same way in SharePoint, and the error handler is not called. Instead what's needed is to implement a subclass of IHttpModule and then configure the web application to load it in response to errors.

To implement the error handler, first create the class ErrorHttpModule shown below. Provide your own implementation of the LogError and LogInfo methods to write to your chosen logger (I recommend log4net):

using System;
using System.Configuration;
using System.Web; 

namespace MikeKnowles.Cobalt
{
    /// <summary>
    /// Generic error handler for a .NET or SharePoint 2007 web application.
    /// </summary>
    public class ErrorHttpModule : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            app.Error += new EventHandler(Application_Error);
        } 

        private void LogError(string message)
        {
            // TODO: write error message and stack trace of exception to your logger
        } 

        private void LogError(string message, Exception ex)
        {
            // TODO: write error message and stack trace of exception to your logger
        } 

        private void LogInfo(string message)
        {
            // TODO: write message to your logger. 
        } 

        public void Application_Error(object sender, EventArgs e)
        {
            Exception ex = HttpContext.Current.Server.GetLastError();
            string message = "HttpContext.Current.Request.Path=" + 
                HttpContext.Current.Request.Path + " "; 

            if (ex != null)
            {
                message += ex.Message;
                ex = ex.GetBaseException();
            }
            else
            {
                message += "HttpContext.Current.Server.GetLastError() is null";
            } 

            LogError(message, ex);
            String errorPagePath = 
                ConfigurationManager.AppSettings["GeneralErrorPath"];

            if (errorPagePath != null)
            {
                LogInfo("Transferring to " + errorPagePath);
                // transfer instead of redirect so the user still 
                // has requested page in address box
                HttpContext.Current.Server.Transfer(errorPagePath);
            }
            else
            {
                LogError("Property GeneralErrorPath not found in web.config.");
            }
        } 

        public void Dispose()
        {
        }
    }
}


Next, register the module with ASP.NET by adding the dll as the first item in the HttpModules section in Web.config. If the module is not listed first then error handling will not work as expected in SharePoint - you will still see the SharePoint generic handler in certain situations even though your error handler was executed. You will need to modify the 4 part name to match the dll you will include the code within (shown below on multiple lines for readability):

<add name="ErrorHttpModule" 
    type="Cobalt, Version=1.0.0.0, 
    Culture=neutral, 
    PublicKeyToken=2a75bd97c63f6363" />

Finally, add the following key to the AppSettings section of Web.config and provide the path to an error page within your web application. If you don't have one, just create a static html page. The property below points to a file in the SharePoint _layouts folder. The error page can reside anywhere you like as long as it's reachable by ASP.NET. Within a SharePoint application, I recommend making this a static file and not a page within the SharePoint database so the error page will be displayed in the event there are problems with the database or connecting to the database:

<add key="GeneralErrorPath" value="/_layouts/FOLDERNAME/GeneralError.aspx" />
Bookmark and Share  Comments [2] | Permalink | SharePoint
 

Today I was working with some batch code that updates the group permissions of several folders within the Site Collection Documents library. Although the code was calling Update() on the SPListItem after modifying the role assignments, when I would run a report to see all content in need of approval it listed every folder which had just been updated. I would then have to click and go through and approve each folder every time the job was run. Makes for lots of impressive sounding mouse click activity, but it gets boring. So today I wrote a function to set the approval status to Approved by the user running the script. Problem solved, no more frenzy of mouse clicks after every content import.

private static void UpdateListItem(SPListItem item)
{
    item.ModerationInformation.Status = 
        SPModerationStatusType.Approved;
    item.ModerationInformation.Comment = 
        "Approved by automated process. User=" + 
        System.Environment.UserName;
    item.Update();
}
Bookmark and Share  Comments [0] | Permalink | SharePoint

Topic: Developing Custom Editors for SharePoint Web Parts

Learn more at www.sharepointsaturday.org/kc

Bookmark and Share  Comments [0] | Permalink | SharePoint
Subscribe
Top 5 Posts
Add Google Analytics to a SharePoint Publishing Site
Integrating jQuery 1.3, ASP.NET 3.5 Visual Studio 2008
Add Custom Table Formats to SharePoint Content Editor
Configuring log4net for SharePoint Windows Authentication
Convert Visual Studio 2008 Class Library Project to Web Application Project
Tags
ASP.NET (5) dasBlog (5) GoogleAnalytics (2) jQuery (1) log4net (2) SharePoint (18) WebDesign (3)
 
 
 
Spreadfirefox Affiliate Button
 
Download Notepad++