Showing posts with label SharePoint. Show all posts
Showing posts with label SharePoint. Show all posts

Wednesday, April 13, 2011

Change order of fields in 'Add new item' for a list

We really needed the order of the fields on the "Add New Item" to change. Here is how we found to do that.

1. Have the list showing
2. Click on List under List Tools on ribbon
3. Click List Settings
4. Under Content Types, click on the Main Content Type. In my case "Task"
5. Click on "Column order"

Friday, October 23, 2009

SharePoint Feature that adds web.config settings and copies SSRS files to IIS folder

I wanted to create SSRS reports for my SharePoint ASP.NET application. I wanted to make everything about the reports to be a part of a Feature to add.

I will have a Web.Config change to make and RDLC files to copy over to the IIS folder with User permissions set.

Here is what I came up with. On FeatureActivated, I call a function to add the Web.Config settings and then another to copy over the RDLC files.

I created an array of SPWebconfigModifications. You will see a couple I commented out so you can get a better feel for how these settings work. I have three modifications occur. The first adds the HTTPHandler needed for the ReportViewer. The second updates that same record if it already exists. I did this because we had a web.config that had the HTTPHandler already but with version 8.0.0.0. So my add child node did not do anything because it was already there. So the update attribute did the trick. The third change is to add a Remove in the appSettings. There is a setting that is automatically added, but it will error in SharePoint, so I've found the answer out there to be to remove that from the appSettings. The only thing I would want to do to upgrade this is to add code to see if the HTTPHandler is already there or not, then only do the first or second modification but not both.

You will notice I am running in Elevated Privileges mode. You will need this unless your feature is a WebApplication feature. Anything lower will not have permission to change web.config settings.

While copying the files over, I first add a folder if not already present. Didn’t want all those RDLC files sitting in the main folder where the web.config file is. There could be one issue here, which is worrying about the URI length. I did not worry about it in this code, but if you want to add that change, please feel free to post back the changes needed.

This is a great way to add all those web.config settings needed for AJAX, just throw them in the array of SPWebconfigModifications and you will have a Feature that loads/unloads the AJAX web.config settings.





using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web.Hosting;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using GroupPublishing.VBSPro.Core;
using System.Diagnostics;
using GroupPublishing.VBSPro.Core.Configuration;
using System.Collections.ObjectModel;
using System.Security.AccessControl;
using System.Security.Principal;

namespace GroupPublishing.VBSPro.Web.FeatureReceivers
{
class VBSProReportsFeatureReceiver : SPFeatureReceiver
{
private static SPWebConfigModification[] WebConfigModifications = {
//new SPWebConfigModification("CallStack", "configuration/SharePoint/SafeMode")
// { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "true" },
//new SPWebConfigModification("mode", "configuration/system.web/customErrors")
// { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "Off" },
//new SPWebConfigModification("debug", "configuration/system.web/compilation")
// { Owner = "motion10DebugSwitch", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "true" },

// Add HttpHandler
new SPWebConfigModification("add[@path='Reserved.ReportViewerWebControl.axd']", "configuration/system.web/httpHandlers")
{ Owner = "FeatureOwner", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode, Value = @"<add verb='*' path='Reserved.ReportViewerWebControl.axd' type='Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' />" },


// Update HttpHandler
new SPWebConfigModification("type", "configuration/system.web/httpHandlers/add[@path='Reserved.ReportViewerWebControl.axd']")
{ Owner = "FeatureOwner", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute, Value = "Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" },


// Add appSetting
new SPWebConfigModification("remove[@key='ReportViewerMessages']", "configuration/appSettings")
{ Owner = "FeatureOwner", Sequence = 0, Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode, Value = @"<remove key='ReportViewerMessages' />" }


};

private static string FolderName = "Reports";

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
//Deploy WebConfig Settings
DeployWebConfigSettings(properties);

//Deploy Report Files
DeployReportFiles(properties);
}
catch (Exception ex)
{
throw new SPException("Error during Activation. See event log for further details");
}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
try
{
// Remove WebConfig Settings
RemoveWebConfigSettings(properties);

// Remove Reports
RemoveReportFiles(properties);

}
catch (Exception ex)
{
throw new SPException("Error during Feature Deactivation. See event log for further details");
}
}

public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
}

public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
}

private void DeployWebConfigSettings(SPFeatureReceiverProperties properties)
{
//Remove any previous WebConfig Settings by this Feature
RemoveWebConfigSettings(properties);

SPSecurity.RunWithElevatedPrivileges(
delegate()
{
String _Owner;
// Get Owner Name
_Owner = properties.Feature.DefinitionId.ToString();

// Get SPSite URL
string spSiteURL = ((SPSite)properties.Feature.Parent).Url;

// Open new instance of SPSIte
using (SPSite oSiteCollection = new SPSite(spSiteURL))
{
// Get SPWebApplication
SPWebApplication webApp = oSiteCollection.WebApplication;

// Add WebConfig Modifications
foreach (SPWebConfigModification modification in WebConfigModifications)
{
modification.Owner = _Owner;
webApp.WebConfigModifications.Add(modification);
}

// Apply Web App Mod
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

// Serialize the web application state and propagate changes across the farm.
webApp.Update();
}
});
}

private void RemoveWebConfigSettings(SPFeatureReceiverProperties properties)
{
SPSecurity.RunWithElevatedPrivileges(
delegate()
{
String _Owner;
// Get Owner Name
_Owner = properties.Feature.DefinitionId.ToString();

// Get SPSite URL
string spSiteURL = ((SPSite)properties.Feature.Parent).Url;

// Open new instance of SPSIte
using (SPSite oSiteCollection = new SPSite(spSiteURL))
{
// Get SPWebApplication
SPWebApplication webApp = oSiteCollection.WebApplication;

// Delete any WebApp Mods for this Feature
Collection<SPWebConfigModification> collection = webApp.WebConfigModifications;
int iStartCount = collection.Count;
// Remove any modifications that were originally created by the owner.
for (int c = iStartCount - 1; c >= 0; c--)
{
SPWebConfigModification configMod = collection[c];
if (configMod.Owner == _Owner)
collection.Remove(configMod);
}

// Apply changes only if any items were removed.
if (iStartCount > collection.Count)
{
Microsoft.SharePoint.Administration.SPFarm.Local.Services.GetValue<Microsoft.SharePoint.Administration.SPWebService>().ApplyWebConfigModifications();
webApp.Update();
}
}
});
}

private void DeployReportFiles(SPFeatureReceiverProperties properties)
{
// first remove report files if they exist
RemoveReportFiles(properties);

SPSecurity.RunWithElevatedPrivileges(
delegate()
{
// Get SPSite URL
string spSiteURL = ((SPSite)properties.Feature.Parent).Url;

// Open new instance of SPSIte
using (SPSite oSiteCollection = new SPSite(spSiteURL))
{
// Get SPWebApplication
SPWebApplication webApp = oSiteCollection.WebApplication;

// loop through each IisSettings dictionary pair (zones [dafault, intranet, internet, etc.] configured for web application)
foreach (KeyValuePair<SPUrlZone, SPIisSettings> pair in webApp.IisSettings)
{
// add folder if not exists
if (!Directory.Exists(pair.Value.Path.FullName.ToString() + @"\" + FolderName))
{
Directory.CreateDirectory(pair.Value.Path.FullName.ToString() + @"\" + FolderName);
}

string[] fileList = System.IO.Directory.GetFiles(SPUtility.GetGenericSetupPath(@"TEMPLATE\FEATURES\VBSProReports\Reports"));
foreach (string fil in fileList)
{
string[] finalFile = fil.ToString().Split('\\');
string _destination = pair.Value.Path.FullName.ToString() + @"\" + FolderName + @"\" + finalFile.GetValue(finalFile.Length - 1).ToString();
File.Copy(fil, _destination, true);

// add security
FileSecurity fs2 = File.GetAccessControl(_destination);
FileSystemAccessRule accessRule = new FileSystemAccessRule("Users", FileSystemRights.ReadAndExecute, AccessControlType.Allow);
fs2.AddAccessRule(accessRule);
File.SetAccessControl(_destination, fs2);
}
}
}
});
}

private void RemoveReportFiles(SPFeatureReceiverProperties properties)
{
SPSecurity.RunWithElevatedPrivileges(
delegate()
{
// Get SPSite URL
string spSiteURL = ((SPSite)properties.Feature.Parent).Url;

// Open new instance of SPSIte
using (SPSite oSiteCollection = new SPSite(spSiteURL))
{
// Get SPWebApplication
SPWebApplication webApp = oSiteCollection.WebApplication;

// loop through each IisSettings dictionary pair (zones [dafault, intranet, internet, etc.] configured for web application)
foreach (KeyValuePair<SPUrlZone, SPIisSettings> pair in webApp.IisSettings)
{
string[] fileList = System.IO.Directory.GetFiles(SPUtility.GetGenericSetupPath(@"TEMPLATE\FEATURES\VBSProReports\Reports"));
foreach (string fil in fileList)
{
string[] finalFile = fil.ToString().Split('\\');
if (Directory.Exists(pair.Value.Path.FullName.ToString() + @"\" + FolderName))
{
string _destination = pair.Value.Path.FullName.ToString() + @"
\" + FolderName + @"\" + finalFile.GetValue(finalFile.Length - 1).ToString();
File.Copy(fil, _destination, true);
// if report exists, delete
if (File.Exists(_destination))
File.Delete(_destination);
}
}
}
}
});
}
}
}




Monday, April 13, 2009

Content Editor Web Part - Relative URLs

When you use the content editor web part, you will find out that when you select an image, it saves it as the absolute URL of that image, even if you type in a relative address. If you remember, you can fix this each time by clicking on "Source Editor..." just under "Rich Text Editor..." in the settings of the content editor web part, and deleting the absolute part of the absolute URLs.

One problem with that (besides all the work and remembering) is if your content will be deployed to the live site, the URLs will still be pointing to the staging environment.

This was obviously not acceptable to our client.

We can fix this by creating an event receiver and overriding the ItemUpdating event of the Pages library.

This is one approach to handling this. There are other approaches to get similar results.

  • Create a project that will hold the SPItemEventReceiver and the SPFeatureReceiver.
  • Create a Site (Web) Feature that will run the SPItemEventReceiver on the Pages library of that Web.
  • Create a Site Collection (Site) Feature that will run the SPFeatureReceiver in which activates/deactivates the Web Feature (B.) on all Webs in the Site Collection.


    Create a project that will hold the SPItemEventReceiver and the SPFeatureReceiver.

    1. Create a new class library project in Visual Studio with the name ContentEditorWebPart_RelativeURLs.
    2. In the new project “Add Reference” to…
      a. Microsoft.SharePoint (which is Windows SharePoint Services in the .NET list)
      b. System.Configuration
      c. System.Web
    3. In the Code Editor, rename the namespace and add the import namespaces
    using System.Web; this is a test. This is only a test. This is still a test of the testing test.
    using System.Web.UI.WebControls.WebParts;
    using System.Configuration;

    namespace ContentEditorWebPart_RelativeURLs
    {

    }



    4. Change the name of the class to ForceRelativeUrlItem and make it inherit from the SPItemEventReceiver class, as follows.
    public class ForceRelativeUrlItem : SPItemEventReceiver
    {

    }


    5. Add the following code within the class to override the ItemUpdating method.
      a. You will see a bunch of Debug lines including a lookup into the web.config, where you can set an AppSetting for if you want the Debug lines to run or not. I like to have these all over for whenever I’m debugging by having DebugView running while running the site. I then turn them off in the web.config when I am done.
      b. We will get down looping through all the web parts in the Pages library looking for all Content Editor Web Parts and then modifying the content by clearing the Site’s URL out of the Content String.
      c. In a later step you will see how this is attached to just the Pages library’s ItemUpdating.
    private static string DebugName = "Debug_ContentEditorWebPart_CS";
    private static bool DebugEnabled = (ConfigurationManager.AppSettings[DebugName] != null ? (ConfigurationManager.AppSettings[DebugName].ToLower().ToString() == "true" ? true : false) : true);

    public override void ItemUpdating(SPItemEventProperties properties)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:Begin"));

    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:DisableEventFiring"));
    // Disable while in this method.
    this.DisableEventFiring();

    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:ListTemplateId: {0}", Int32.Parse(properties.ListItem.ParentList.BaseTemplate.ToString())));

    // get a reference to the list item (the page in this case)
    SPListItem _SPListItem = properties.ListItem;
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:SPListItem:Name: {0}", _SPListItem.Name));

    // get a reference to the containing SPWeb
    using (SPWeb _SPWeb = _SPListItem.Web)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:SPWeb:Name: {0}", _SPWeb.Name));

    // get a reference to the the web part manager on the page
    using (SPLimitedWebPartManager _SPLimitedWebPartManager = _SPWeb.GetLimitedWebPartManager(_SPListItem.Url, PersonalizationScope.Shared))
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:SPLimitedWebPartManager:Count: {0}", _SPLimitedWebPartManager.WebParts.Count));

    // loop through all of the web parts on the page and update
    // all of the CEWP's
    Microsoft.SharePoint.WebPartPages.SPLimitedWebPartCollection LimitedWebParts1 = _SPLimitedWebPartManager.WebParts;
    foreach (System.Web.UI.WebControls.WebParts.WebPart _WebPart in LimitedWebParts1)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:WebPart:DisplayTitle: {0} Type: {1}", _WebPart.DisplayTitle, _WebPart.GetType().ToString()));
    // if WebPart is a CEWP
    if (_WebPart.GetType().Equals(typeof(ContentEditorWebPart)))
    {
    using (ContentEditorWebPart _ContentEditorWebPart = (ContentEditorWebPart)_WebPart)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:ContentEditorWebPart:DisplayTitle: {0}", _ContentEditorWebPart.DisplayTitle));

    // get the contents of the CEWP
    string _ContentString = _ContentEditorWebPart.Content.InnerText;
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:ContentEditorWebPart:Content(Before): {0}", _ContentString));

    // remove the absolute url
    _ContentString = _ContentString.Replace(_SPWeb.Site.RootWeb.Url, "");
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:ContentEditorWebPart:Content(After): {0}", _ContentString));
    // create an Xml element to use to update the CEWP
    XmlDocument _XmlDocument = new XmlDocument();
    XmlElement _XmlElement = _XmlDocument.CreateElement("MyElement");
    _XmlElement.InnerText = _ContentString;
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:XmlElement:InnerText: {0}", _XmlElement.InnerText));

    // update the Content property of the CEWP
    _ContentEditorWebPart.Content = _XmlElement;
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:ContentEditorWebPart:Change Content"));

    try
    {
    // Save the changes
    _SPLimitedWebPartManager.SaveChanges(_ContentEditorWebPart);
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:ItemUpdating:ContentEditorWebPart:Updated"));
    }
    catch (Exception ex)
    {
    Debug.Write("Content Web Part - Relative URLs Feature \n " + ex.Message);
    }
    }
    }
    }
    }
    }
    // Enable Event Firing again
    this.EnableEventFiring();
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP:Ending"));


    6. Add another class, as follows.
    public class FeatureEventHandler : SPFeatureReceiver
    {

    }


    7. Add the following code within the class to override the Feature methods.
    private static string DebugName = "Debug_ContentEditorWebPart_CS";
    private static bool DebugEnabled = (ConfigurationManager.AppSettings[DebugName] != null && ConfigurationManager.AppSettings[DebugName].ToLower().ToString() == "true" ? true : false);

    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureActivated:Begin"));
    // get the Site Collection
    using (SPSite site = SPContext.Current.Site)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureActivated:Site:URL: {0}", site.Url));
    // loop through all Webs in the Site Collection
    foreach (SPWeb web in site.AllWebs)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureActivated:Web(before):Name {0} Feature Cnt: {1}", web.Name, web.Features.Count));
    // Activate ContentEditorWebPart_RelativeURLs Feature on that particular Web
    web.Features.Add(new Guid("A2184210-B18E-4331-B029-CE55A2487328"), true);
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureActivated:Web(after):Name {0} Feature Cnt: {1}", web.Name, web.Features.Count));
    }
    }
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureActivated:End"));
    }

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureDeactivating:Begin"));
    // get the Site Collection
    using (SPSite site = SPContext.Current.Site)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureDeactivating:Site:URL: {0}", site.Url));
    // loop through all Webs in the Site Collection
    foreach (SPWeb web in site.AllWebs)
    {
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureDeactivating:Web(before):Name {0} Feature Cnt: {1}", web.Name, web.Features.Count));
    // Activate ContentEditorWebPart_RelativeURLs Feature on that particular Web
    web.Features.Remove(new Guid("A2184210-B18E-4331-B029-CE55A2487328"));
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureDeactivating:Web(after):Name {0} Feature Cnt: {1}", web.Name, web.Features.Count));
    }
    }
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureDeactivating:End"));
    }

    public override void FeatureInstalled(SPFeatureReceiverProperties properties)
    {
    // Do nothing
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureInstalled:Begin"));
    }

    public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
    {
    // Do Nothing
    if (DebugEnabled) Debug.WriteLine(string.Format("CEWP_SA:FeatureUninstalling:Begin"));
    }


    8. In Solution Explorer, right-click the ContentEditorWebPart_RelativeURLs node, and then click Properties.
    9. In the Properties dialog box, click the Signing tab, select Sign the assembly, select Choose a strong name key file, and then click .
    10. In the Create Strong Name Key dialog box, type ContentEditorWebPart_RelativeURLs.snk in the Key file name box, and then click OK.
    11. Find the \ ContentEditorWebPart_RelativeURLs \bin\Debug folder in the Visual Studio Projects folder, and drag the ContentEditorWebPart_RelativeURLs.dll file to Local_Drive:\WINDOWS\assembly to place the DLL in the global assembly cache.
    12. I always do an IISRESET at this point.

    Create a Site (Web) Feature that will run the SPItemEventReceiver on the Pages library of that Web.

    1. Create a folder in Local_Drive:/Program Files/Common Files/Microsoft Shared/web server extensions/12/TEMPLATE/FEATURES called ContentEditorWebPart_RelativeURLs.
    2. Create a Feature.xml Files file in this folder like the following that identifies the Feature and its element manifest file and sets the Feature scope to Web site.
    <Feature
    DefaultResourceFile="core"
    Description="Changes absolute urls to relative urls for all Content Editor Web Parts used in the Pages library."
    Id="GUID"
    Hidden="False"
    Scope="Web"
    Title="Content Editor Web Part - Force Relative Url"
    Version="1.0.0.0"
    xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
    <ElementManifest Location="elements.xml"/>
    </ElementManifests>
    </Feature>


    3. To replace the GUID placeholder in the previous Id attribute, generate a GUID by running guidgen.exe located in Local_Drive:\Program Files\Microsoft Visual Studio 8.
    4. Create an Elements.xml file in the ContentEditorWebPart_RelativeURLs folder that identifies the assembly, class, and method to implement as the event handler. This example applies the event handler to the Pages library of a web, as specified by the ListTemplateId attribute (850).
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <Receivers ListTemplateId="850">
    <Receiver>
    <Name>CEWP ItemUpdating - Relative URLs</Name>
    <Type>ItemUpdating</Type>
    <SequenceNumber>10000</SequenceNumber>
    <Assembly>ContentEditorWebPart_RelativeURLs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fb6673b46adc9058</Assembly>
    <Class>ContentEditorWebPart_RelativeURLs.ForceRelativeUrlItem</Class>
    <Data></Data>
    <Filter></Filter>
    </Receiver>
    </Receivers>
    </Elements>


    5. To get the Public Key Token of the assembly, in Windows Explorer find the ContentEditorWebPart_RelativeURLs.dll file in the Local_Drive:\WINDOWS\assembly, right-click the file, click Properties, and on the General tab of the Properties dialog box, select and copy the token.
    6. At a command prompt, navigate to \Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN on the local drive, and type each of the following commands to install the Feature in the deployment. We will activate the Feature inside the third main step (below) :
    Stsadm –o installfeature –filename ContentEditorWebPart_RelativeURLs\feature.xml -force


    Create a Site Collection (Site) Feature that will run the SPFeatureReceiver in which activates/deactivates the Web Feature (B.) on all Webs in the Site Collection.

    1. Create a folder in Local_Drive:/Program Files/Common Files/Microsoft Shared/web server extensions/12/TEMPLATE/FEATURES called ContentEditorWebPart_RelativeURLs_SiteActivation.
    2. Create a Feature.xml Files file in this folder like the following that identifies the Feature and its Receiver information and sets the Feature scope to Site (Site Collection).
    <Feature
    Description="Activates the Web Feature 'Content Editor Web Part - Force Relative Url' to all webs on the farm."
    Hidden="False"
    Id="GUID"
    ReceiverAssembly="ContentEditorWebPart_RelativeURLs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxxx"
    ReceiverClass="ContentEditorWebPart_RelativeURLs.FeatureEventHandler"
    Scope="Site"
    Title="Content Editor Web Part - Force Relative Url - Activate All"
    Version="1.0.0.0"
    xmlns="http://schemas.microsoft.com/sharepoint/">
    </Feature>


    3. To get the Public Key Token of the assembly, in Windows Explorer find the ContentEditorWebPart_RelativeURLs_SiteActivation.dll file in the Local_Drive:\WINDOWS\assembly, right-click the file, click Properties, and on the General tab of the Properties dialog box, select and copy the token.
    4. To replace the GUID placeholder in the previous Id attribute, generate a GUID by running guidgen.exe located in Local_Drive:\Program Files\Microsoft Visual Studio 8. I just used the same GUID as before, just changing the last number by 1.
    5. At a command prompt, navigate to \Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN on the local drive, and type each of the following commands to install the Feature in the deployment, activate the feature, and activate the other feature in all Webs :
    Stsadm –o installfeature –filename ContentEditorWebPart_RelativeURLs_SiteActivation \feature.xml –force

    stsadm -o activatefeature -filename ContentEditorWebPart_RelativeURLs_SiteActivation \Feature.xml -url http://Server/Site

    iisreset




    You will now notice that your URLs in the Content Editor Web Parts are all saved as Relative URLs.

    Here are the main sites that got me through this. If you look into them, you will notice the first site addresses this directly, but from a slightly different angle.

    http://www.devcow.com/blogs/jdattis/archive/2007/09/27/11463.aspx

    http://msdn.microsoft.com/en-us/library/ms453149.aspx

    http://www.u2u.info/Blogs/Patrick/Lists/Posts/Post.aspx?ID=1567
  • Tuesday, April 7, 2009

    This item cannot be deleted because it is still referenced by other pages. (Page Layout or Master Page)

    I have moved all references to a Page Layout to a different Page Layout. But when I try to delete the Page Layout, I get this error.

    "This item cannot be deleted because it is still referenced by other pages”

    After spending hours trying to modify my code, thinking I'm missing references somewhere, I finally searched around for this error. I found that it is a Microsoft known bug. Their workaround is to check the Hidden Page in the properties of the Page Layout or Master Page, and if it is currently a Master Page, change it to a Page Layout. That will keep it from being selectable when creating a page.

    But, here is the real workaround that I found out there...

    1. Create a folder inside the Master Page Gallery.
    2. Move the Page Layout or Master Page you don't want and are not using anymore.
    3. Delete the folder.

    I can only guess at why it works, but it does, and I'm glad someone out there thought to try such a thing.

    Monday, March 16, 2009

    Activating a feature from STSADM


    If you have a feature that times-out when you try to activate it in the Site Settings on the web, then you will need to activate it on the command line.


    1.Move to the 12 hive.
    a.Unless you have installed WSS someplace other than the default,
    you can find the 12 hive at
    C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\
    2.Make sure your feature is in the subfolder \TEMPLATE\FEATURES\
    inside the 12 hive.
    3.Move into the bin folder.
    4.Run the STSADM command to activate the feature.
    STSADM -o ActivateFeature -Url {your site}
    -FileName {the feature.xml inside your feature}
    EX:
    STSADM -o ActivateFeature -Url http://www.thesite.com/ 
    -FileName My_DirectCustomer\feature.xml
    5.If you've made core changes to your feature, you'll need to deactivate and reactivate. Do the same command only with DeactivateFeature first.

    Friday, March 6, 2009

    Replacing one site column with another


    We have a SharePoint list of author pages that use a particular PageLayout. This PageLayout includes a content type that includes site columns named FirstName and LastName.

    We wanted to have this list of pages be sortable by the Author’s first or last name. The problem is someone that was on this project that is no longer a part of this project made those two fields as Publishing HTML types instead of Single Line of Text types. Who knows why. But Publishing HTML types are not sortable as display columns of a list.

    So we wanted to convert the columns to Single Line of Text. We discovered it was not as easy as just going to the site column and changing it to the Single Line of Text. We could not do it in the UI so we tried in code. We could do it, but what it did was to change the master site column but then detached everywhere that used that column and made local columns that were Publishing HTML so it would not break the content in those fields.

    So instead here are the steps we took to complete this…


    1.Create two new site columns, First_Name and Last_Name.
    a.Go to Site Actions -> Site Settings -> Modify All Site Settings (make sure you are at the top level of the site)
    b.Go to Site columns under Galleries
    c.Click Create
    d.Enter column name, select Single Line of Text, and put into whatever Group you want. Click OK
    e.Repeat c and d for second field
    2.Add site columns to the Content Type that is used on the PageLayout you want to modify
    a.Go to Site Content Types under Galleries
    b.Select the right Content Type
    c.Under Columns, click Add from existing site columns
    d.Add the two new site columns
    3.Create console program (my code can be found at the bottom)
    a.Must first find the content type that you want to change
    b.Then loop through all sites looking for all Lists that use that content type
    c.Copy over data looping through each row of each list found
    4.Find all User Controls that have the old field and replace with the new field, including any code that references that field. My code will pump out all the Web – List – Items that were copied to a log file, so you can use that list to help find which user controls use those fields.



    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.SharePoint;
    using System.IO;
    using Microsoft.SharePoint.Publishing;
    using Microsoft.SharePoint.Publishing.Fields;

    namespace SiteColumnSwitch
    {
    class Program
    {
    static void Main(string[] args)
    {
    // -----------------------------------------
    // Declare VARs
    // -----------------------------------------
    string siteUrl = "http://mysite";
    string contentType = "Author";
    string oldSiteColumnName = "LastName";
    string newSiteColumnName = "Last_Name";

    // -----------------------------------------
    // Call converter
    // -----------------------------------------
    SwapSiteColumns converter = new SwapSiteColumns();
    using (SPSite siteColl = new SPSite(siteUrl))
    {
    try
    {
    converter.Swap_SiteColumns(siteColl, contentType, oldSiteColumnName, newSiteColumnName);
    }
    catch (ApplicationException ex)
    {
    Console.WriteLine(ex.Message);
    }
    }
    }
    }

    class SwapSiteColumns
    {
    string FileLocation = @"C:\Data.txt"; // where to store log file

    // -----------------------------------------
    // Log settings
    // -----------------------------------------
    internal void Log(Exception e)
    {
    using (StreamWriter file = new StreamWriter(FileLocation, true))
    { file.WriteLine(e.Message); }
    Console.WriteLine(e.Message);
    }
    internal void Log(string LogData)
    {
    using (StreamWriter file = new StreamWriter(FileLocation, true))
    { file.WriteLine(LogData); }
    Console.WriteLine(LogData);
    }
    internal void Log(bool Clear)
    {
    if (Clear)
    {
    TextWriter tw = new StreamWriter(FileLocation);
    tw.Write("");
    tw.Close();
    }

    }

    internal void Swap_SiteColumns(SPSite site, string contentType, string oldSiteColumnName, string newSiteColumnName)
    {
    // -----------------------------------------
    // Declare VARs
    // -----------------------------------------
    SPContentType cType = null;
    SPField oldField = null;
    SPField newField = null;
    Log(true);

    // -----------------------------------------
    // Get ContentType info
    // -----------------------------------------
    foreach (SPWeb web in site.AllWebs) // look in all webs until found
    {
    foreach (SPContentType contType in web.ContentTypes) // check each content type in each web until found
    {
    if (contentType == contType.Name.ToString())
    {
    cType = contType;
    break; // found content type
    }

    }
    if (null != cType) // if content type found, search for site columns in content type
    {
    foreach (SPField cField in cType.Fields)
    {
    if (cField.InternalName == oldSiteColumnName) { oldField = cField; }
    if (cField.InternalName == newSiteColumnName) { newField = cField; }
    }

    if (null == oldField || null == newField) // if not found by Internal Name look for by Display Name

    {
    foreach (SPField cField in cType.Fields)
    {
    if (cField.Title == oldSiteColumnName && oldField == null) { oldField = cField; }
    if (cField.Title == newSiteColumnName && newField == null) { newField = cField; }
    }
    }

    if (null != oldField && null != newField)
    {
    break; // found both old and new site columns
    }
    }
    }

    // -----------------------------------------
    // Find where content type is used and copy data.
    // -----------------------------------------
    if (null != oldField && null != newField)
    {
    foreach (SPWeb web in site.AllWebs) // check in all webs
    {
    Swap_Data(web, cType, oldField, newField); // swap data if found
    }
    }
    else // site columns were not found
    {
    if (null == oldField) { Log(string.Format("Could not find site column: {0}", oldSiteColumnName)); }
    if (null == newField) { Log(string.Format("Could not find site column: {0}", newSiteColumnName)); }
    }
    }

    internal void Swap_Data(SPWeb web, SPContentType cType, SPField oldField, SPField newField)
    {
    if (null != oldField && null != newField)
    {
    SPListCollection webLists = web.Lists; // get Lists from web
    foreach (SPList wList in webLists) // check each list in web
    {
    foreach (SPContentType contType in wList.ContentTypes) // check each content type of a list
    {
    if (contType.Id.Parent == cType.Id contType.Id == cType.Id) // if the content type or it's parent is the right content type
    {
    MoveFieldsInList(wList, oldField, newField); // move data
    }
    }
    }
    }
    }

    private void MoveFieldsInList(SPList wList, SPField oldField, SPField newField)
    {
    foreach (SPListItem lItem in wList.Items) // each item (record) in the List
    {
    SPFieldText fld = lItem.Fields[newField.Id] as SPFieldText;
    string fldValue = fld.GetFieldValueAsText(lItem[newField.Title]); // value already in new field
    string fldValueNew = fld.GetFieldValueAsText(lItem[oldField.Title]); // value to be put in new field
    if (fldValueNew != fldValue) // if value needs to be replaced
    {
    SPFile liFile = lItem.File;
    try // check item out
    {
    liFile.CheckOut();
    }
    catch (SPException ex)
    { // check it in first, then check it out
    liFile.CheckIn("forced check in to update item");
    liFile.CheckOut();
    }

    lItem[newField.Title] = fldValueNew; // set the new value in the field
    Log(string.Format("Parent web: {0} List: {1} Item: {2} Value: {3} New Value: {4}", wList.ParentWeb.Title, wList.Title, lItem.DisplayName, fldValue, fldValueNew));

    try
    {
    lItem.Update();
    }
    catch (Exception ex)
    {
    Log(string.Format("{0} : {1}", ex.GetType(), ex.Message));
    }
    try
    {
    liFile.Update();
    }
    catch (Exception ex)
    {
    Log(string.Format("{0} : {1}", ex.GetType(), ex.Message));
    }
    try
    {
    liFile.CheckIn("Auto-checkin from updating item."); // check the file in
    }
    catch (Exception ex)
    {
    Log(string.Format("{0} : {1}", ex.GetType(), ex.Message));
    }

    // these steps are dependent on how your environment is set up (Publish and Approve)
    liFile.Publish("Auto-checkin from updating item.");
    try
    {
    liFile.Approve("Auto-checkin from updating item.");
    }
    catch (Exception ex)
    {
    Log(string.Format("Could not auto approve " + liFile.Name));
    System.Diagnostics.EventLog.WriteEntry("eCommerce", "Could not auto approve " + liFile.Name);
    }
    }
    }
    }
    }

    }

    Friday, February 27, 2009

    Multiple Field Custom Validator

    I needed to extend the Custom Validator to allow it to have multiple ControlToValidate fields, so that if any of the fields are changed the validator would trigger.

    Example:

    Credit Card Expiration Date - I had two dropdownlists for the month and year. I wanted a custom validator that instantly triggered a warning if the two fields did not combine into a date that was current or future.

    I was able to create the JavaScript on the client side to check for that, but I would have to have 2 custom validators, one for each dropdownlist. So if I selected a new year but the combined was not current or future then the warning would show. But if I then changed the month and the combined was now current or future, it would not know to go back to the previous custom validator and validate it, since it had its own validator.

    I know I could have hard coded the JavaScript to do it, but I wanted something more substantial and reusable.

    So I decided to create a multi-field custom validator, just extending the .NET Custom Validator.


    Here are the steps I took to create this.

    1. Start a new class project.

    2. Create the new class inheriting from System.Web.UI.WebControls.CustomValidator. Also includes settings for the control.

    using System;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Security.Permissions;
    using System.ComponentModel;
    using System.Drawing;

    namespace My.CustomControls
    {
       [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
       [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
       [ToolboxData(@"<{0}:MultipleFieldsValidator runat=server></{0}:MultipleFieldsValidator>")]
       public class MultipleFieldsValidator : System.Web.UI.WebControls.CustomValidator
       {
       }
    }

    3. Add a Private Property to store the extra controls to validate.
            #region Private Properties

       private string _extraControlsToValidate;

    #endregion

    4. Add a Public Property to Get/Set the extra controls to validate.

    #region Public Properties

       /// <summary>
       /// Comma separated list of control IDs that you want to check
       /// </summary>
       [Browsable(true)]
       [Category("Behavior")]
       [Themeable(false)]
       [DefaultValue("")]
       [Description("Comma separated list of extra control IDs that you want to check")]
       public string ExtraControlsToValidate
       {
         get
         {
           return _extraControlsToValidate;
         }
         set
         {
           _extraControlsToValidate = value;
         }
       }

    #endregion

    5. Create some helper methods to parse out that field.

    #region Helper Methods

    private string[] GetControlsToValidateIDs()
    {
       try
       {
         string[] controlToValidateIDs = null;
         if (this.ExtraControlsToValidate != null && this.ExtraControlsToValidate.Length > 0)
         {
           string controlsToValidate = this.ExtraControlsToValidate.Replace(" ", "");
           if (controlsToValidate.Length > 0)
           {
             try
             {
               controlToValidateIDs = controlsToValidate.Split(',');
             }
             catch (ArgumentOutOfRangeException ex)
             {
               throw new FormatException(string.Format("The ExtraControlsToValidate property of {0} is not well-formatted.", this.ID), ex);
             }
           }
         }
         return controlToValidateIDs;
       }
       catch (Exception e)
       {
         throw (e);
       }
    }

    // Only needed if doing the RegisterExpandoAttribute in AddAttributesToRender
    private string GenerateClientSideControlsToValidate()
    {
       try
       {
         string[] controlToValidateIDs = this.GetControlsToValidateIDs();
         string controlToValidateIDTrimmed;
         string controlRenderIDs = string.Empty;
         foreach (string controlToValidateID in controlToValidateIDs)
         {
           controlToValidateIDTrimmed = controlToValidateID.Trim();
           if (controlToValidateIDTrimmed == string.Empty)
           {
             throw new FormatException(string.Format("The ExtraControlsToValidate property of {0} is not well-formatted.", this.ID));
           }
           controlRenderIDs += "," + base.GetControlRenderID(controlToValidateIDTrimmed);
         }
         controlRenderIDs = controlRenderIDs.Remove(0, 1); // Removing the first ","
         return controlRenderIDs;
       }
       catch (Exception e)
       {
         throw (e);
       }
    }

    #endregion

    6. Override a method (OnPreRender) to attach the validator to the extra controls.

    #region Overiden Methods

    protected override void OnPreRender(EventArgs e)
    {
       try
       {
         base.OnPreRender(e);
         if (base.RenderUplevel)
         {
           if (!Page.ClientScript.IsClientScriptBlockRegistered("MultipleFieldsValidator"))
           {
             if (null != this.ExtraControlsToValidate && this.ExtraControlsToValidate.Length > 0)
             {
               System.Text.StringBuilder sb = new System.Text.StringBuilder();
               sb.Append("<script language='javascript' type='text/javascript'> \n");
               sb.Append("<!-- \n");
               sb.Append("var ctrl=''; \n");
               sb.Append("var vld=''; \n");
               string[] extraControls = GetControlsToValidateIDs();
               foreach (string controlToValidateID in extraControls)
               {
                 string controlToValidateIDTrimmed = controlToValidateID.Trim();
                 if (controlToValidateIDTrimmed != string.Empty)
                 {
                   sb.Append("ctrl = document.all ? document.all['" + base.GetControlRenderID(controlToValidateIDTrimmed) + "'] : document.getElementById('" + base.GetControlRenderID(controlToValidateIDTrimmed) + "'); \n");
                   sb.Append("vld = document.all ? document.all['" + this.ClientID + "'] : document.getElementById('" + this.ClientID + "'); \n");
                   sb.Append("if (typeof(ctrl) != 'undefined') \n");
                   sb.Append("{ \n");
                   sb.Append(" ValidatorHookupControl(ctrl, vld); \n");
                   sb.Append("} \n");
                 }
               }
               sb.Append("--> \n");
               sb.Append("</script> \n");
               this.Page.ClientScript.RegisterStartupScript(this.GetType(), "MultipleFieldsValidator", sb.ToString());
             }
           }
         }
       }
       catch (Exception ex1)
       {
         throw (ex1);
       }
    }

    #endregion

    7. (Optional) Override a method (AddAtrributesToRender) to put a Custom (Expando) Attribute on the validator control if the developer wants access to that in the client side function they add to the custom validator.

    #region Overiden Methods

    protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
    {
       try
       {
         base.AddAttributesToRender(writer);
         if (this.RenderUplevel)
         {
           string clientID = this.ClientID;
           if (this.ExtraControlsToValidate != null && this.ExtraControlsToValidate.Length > 0)
           {
             Page.ClientScript.RegisterExpandoAttribute(clientID, "extracontrolstovalidate", this.GenerateClientSideControlsToValidate());
           }
         }
       }
       catch (Exception ex1)
       {
         throw (ex1);
       }
    }

    #endregion

    8. (Optional) Add a png file to the project for use in the toolbox. Right click on it, properties, Build Action, select “Embedded Resource”. Then add this setting to the class.

    [ToolboxBitmap(typeof(MultipleFieldsValidator), "MultiFieldValidator.png")]
    public class MultipleFieldsValidator : System.Web.UI.WebControls.CustomValidator

    9. (Optional, but required if putting in the GAC or bin folder for SharePoint) Strongly type the project. Right click on solution name, properties. Signing, Choose a strong name key file, select new, Enter Key file name, uncheck “Protect my key file with a password unless you want to have that.

    10. Build the project.

    11. Copy DLL to your main project’s bin folder or to the GAC if you have problems with permissions and don’t have time to modify the permission policy.

    12. Add a reference to the DLL in your main project.

    13. (Optional) While opened to a web page (aspx, ascx, etc), right click on a header in the Toolbox and click on Choose Items, “.NET Framework Components”, Browse. Find the DLL and select it, then OK. This will add the new MultiFieldCustomValidator to your Toolbox.

    14. If added to Toolbox, drag and drop onto webpage where you want the control. It will add a Register directive at the top of the page. If you don’t add to the Toolbox, you need to first add the Register directive, then:
    <%@ Register Assembly="My.CustomControls" Namespace="My.CustomControls" TagPrefix="cc3" %>

    (Optional) If the full assembly is needed like for SharePoint then:
    <%@ Register Assembly="My.CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxxx" Namespace="My.CustomControls" TagPrefix="cc3" %>

    15. (Optional) If using SharePoint, then add a “SafeControl” to the web.config so SharePoint trusts this DLL.
    <SafeControls>
       <SafeControl Assembly="My.CustomControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4abcb93e1cb47678" Namespace="My.CustomControls" TypeName="*" Safe="True" />
    </SafeControls>

    16. Add the properties to the control just like you would with the CustomValidator, but add one extra property, ExtraControlsToValidate, which is a comma delimited list of all the ID’s of the extra controls you want attached to this validator.

    Now you have your Multi-Field Custom Validator ready to use in any of your projects.

    Friday, February 6, 2009

    SSL for only some pages in SharePoint

    I had a client that wanted SSL for only a few of the pages on their website, Login Page, all Checkout pages, and Account Management pages. But they didn't want SSL on all the other pages because they want the pages to load as fast as possible.

    After much research I found one blog that really helped me get this accomplished.
    http://sanmuki.blogspot.com/2008/07/enable-ssl-for-selected-pages-in-moss.html

    I'll list below the steps I needed to take to make this happen.

    Usually you have to find a way to handle the session state when switching between HTTP and HTTPS, but MOSS already has that ability.

    1. First create a class that will be used to check if URLs need to be SSL'ed or not and redirect URLs as needed.
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Web.Security;
    using System.Diagnostics;
    using System.Web.UI;

    namespace Project.Business
    {
    public class ForceSSL : System.Web.IHttpModule
    {
    #region IHttpModule Members

    public void Dispose()
    {
    //throw new NotImplementedException();
    }

    public void Init(System.Web.HttpApplication context)
    {
    context.BeginRequest += new EventHandler(context_BeginRequest);
    }

    #endregion

    void context_BeginRequest(object sender, EventArgs e)
    {
    RedirectSSL(System.Web.HttpContext.Current.Request.Url.PathAndQuery);
    }

    /// <summary>
    /// Method to switch between SSL and non-SSL.
    /// </summary>
    /// <param name="pageURL">URL that needs to be looked up for SSL requirement.</param>
    /// <returns></returns>
    public void RedirectSSL(string pageURL)
    {
    //---------------------------------------------------
    // Init VARs
    //---------------------------------------------------
    bool found = false;
    //--- Get Pages from web.config that need to be SSL'ed
    string sslPages = System.Configuration.ConfigurationSettings.AppSettings["SSLPages"];

    if (!(null == sslPages))
    {
    //---------------------------------------------------
    // Parse out the list of Pages that need SSL
    //---------------------------------------------------
    char[] separator;
    separator = new char[] { ',' };
    string[] pages;
    pages = sslPages.Split(separator);
    for (int i = 0; i < pages.Length; i++)
    {
    //---------------------------------------------------
    // Check if URL passed to method is in the list
    //---------------------------------------------------
    if (pageURL.ToLower().Contains(pages[i].ToLower()))
    {
    found = true;
    break;
    }
    }
    }

    if (found) //-- If page needs to be SSL
    {
    //---------------------------------------------------
    // If page needs to be SSL but is not already set as SSL
    //---------------------------------------------------
    if (System.Web.HttpContext.Current.Request.Url.Scheme.ToString() == "http")
    {
    //---------------------------------------------------
    // Init VARs. Get the SSLPort from web.config
    //---------------------------------------------------
    string sURL = System.Web.HttpContext.Current.Request.Url.ToString();
    string sPort = System.Web.HttpContext.Current.Request.Url.Port.ToString();
    string sslPort =System.Configuration.ConfigurationSettings.AppSettings["SSLPort"];
    //-- getting rid of the double slashes to make this process easier.
    sURL = sURL.Replace("http://", "http");
    //-- if SSL is on port 443, then the :443 is not needed, but anything else it is need (i.e. :444)
    if ((null == sslPort) || sslPort == "443")
    {
    sslPort = "";
    }
    else
    {
    sslPort = ":" + sslPort;
    }
    //-- if coming from a http port other than 80 then replace with new port else add the new port
    if (sURL.IndexOf(":") > 0)
    {
    sURL = sURL.Replace(":" + sPort, sslPort);
    }
    else
    {
    sURL = sURL.Substring(0, sURL.IndexOf("/")) + sslPort + sURL.Substring(sURL.IndexOf("/"));
    }
    //-- add back the double slashes and include the "s" to http
    sURL = sURL.Replace("http", "https://");
    //-- debugging to know it was redirected
    Debug.WriteLine("Redirected SSL URL: " + sURL);
    //-- redirect to the new URL
    System.Web.HttpContext.Current.Response.Redirect(sURL);
    }
    }
    else //-- If pages needs not be SSL
    {
    //---------------------------------------------------
    // If page needs not be SSL but is SSL currently
    //---------------------------------------------------
    if (System.Web.HttpContext.Current.Request.Url.Scheme.ToString() == "https")
    {
    //---------------------------------------------------
    // Init VARs. Get the non-SSL port from web.config
    //---------------------------------------------------
    string sURL = System.Web.HttpContext.Current.Request.Url.ToString();
    string sslPort = System.Web.HttpContext.Current.Request.Url.Port.ToString();
    string sPort = System.Configuration.ConfigurationSettings.AppSettings["NonSSLPort"];
    //-- getting rid of the double slashes to make this process easier.
    sURL = sURL.Replace("https://", "https");
    //-- if non-SSL is on port 80, then the :80 is not needed, but anything else it is need (i.e. :81)
    if ((null == sPort) || sPort == "80")
    {
    sPort = "";
    }
    else
    {
    sPort = ":" + sPort;
    }
    //-- if coming from a http port other than 443 then replace with new port else add the new port
    if (sURL.IndexOf(":") > 0)
    {
    sURL = sURL.Replace(":" + sslPort, sPort);
    }
    else
    {
    sURL = sURL.Substring(0, sURL.IndexOf("/")) + sPort + sURL.Substring(sURL.IndexOf("/"));
    }
    //-- add back the double slashes and exclude the "s" from https
    sURL = sURL.Replace("https", "http://");
    //-- debugging to know it was redirected
    Debug.WriteLine("Redirected non-SSL URL: " + sURL);
    //-- redirect to the new URL
    System.Web.HttpContext.Current.Response.Redirect(sURL);
    }
    }
    }
    }
    }


    2. Build this code, sign the assembly and put it in the GAC.

    3. Add lines to web.config...

    <httpModules>
    <add name="ForceSSL" type="Cook.Business.ForceSSL, Cook.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx"/>
    </httpModules>



    <appSettings>
    <!-- Switching between HTTP/HTTPS -->
    <add key="SSLPort" value="443"/>
    <add key="NonSSLPort" value="2222"/>
    <add key="SSLPages" value="/Pages/UserLogin.aspx,/Checkout/CheckoutPayment.aspx,/Checkout/CheckoutShipping.aspx,/Checkout/CheckoutReview.aspx,/Checkout/CheckoutConfirmation.aspx"/>
    </appSettings>



    4. We need an SSL certificate in order to use SSL.
      a. If you already have an SSL certificate on the server move on to #5
      b. To create your own certificate you can use SelfSSL from Microsoft
        b1. Download the IIS 6.0 Resource Kit Tools
        b2. Install the tools on the server
        b3. Run SelfSSL. Make sure you put the length of time out farther than a week (my mistake) so it will last as long as your project will last.

    5. Now we need to set up the website in IIS for SSL.
      a. Go to Start -> Administrative Tools -> Internet Information Services (IIS) Manager
      b. Expand the IIS server
      c. Expand the Web Sites folder
      d. Go to properties of the website
      e. Inside properties, navigate to Directory Settings -> Secure Communications -> Server Certificate
      f. Assign the existing certificate. This is from #4

    6. Inside Directory Security, go to secure communications -> edit, and make sure Require secure channel (SSL) is NOT checked.

    7. On the Web Site tab (same level as Directory Security) add an SSL port. The default is 443. If you use 80 and 443 for TCP port and SSL port respectively then your URL will not need the ports mentioned in the URL, but the code above handles that if you do.

    8. The post that I learned this from needed one more step that I did not need. ***OK I was wrong. I did not need this on my development server, but I did on the live server. The difference seems to be FQN (Fully Qualified Name). Without this step, I'd get sqlsessionstateresolver errors much of the time on the live server.
      a. Go to the Central Administration website
      b. Go to Operations -> Alternate Access Mappings
      c. Add the new URL via internal URL's link.
    https://servername/

    9. Perform an IISReset
      a. Go to a command prompt and type iisreset

    You should now be able to go on to your website and see the pages in your web.config list are SSL while everything else is not SSL.