My blog has moved to http://www.ascentium.com/blog/sp/default.aspx

For SPDeploy information visit http://www.ascentium.com/blog/sp/Post29.aspx

Thursday, September 27, 2007

SPLongOperation

Want to see the nice SharePoint spinner while you perform long running operations? Here's how:

using (SPLongOperation op = new SPLongOperation(this.Page)) {
op.LeadingHTML = "Please wait";
op.Begin();
try {
// Do some stuff that takes a long time
op.End("Finished.aspx");
} catch (Exception ex) {
op.End("Error.aspx");
}
}

When you call op.Begin() you'll see the SharePoint spinner until you call op.End(). Sweet eh? Notice that I am using a try/catch block, if you don't do this and an error occurs then the user will be stuck in perpetual spinner land. To avoid this send the user to an error page when an error happens.

Tuesday, September 25, 2007

Introducing SPDeploy

imageSPDeploy is a tool that allows developers to create SharePoint customizations inside Visual Studio, then package and deploy those changes to a remote SharePoint server. With SPDeploy you can create a project that contains SharePoint customizations and deploy those customizations to a remote SharePoint server in 10 minutes or less. SPDeploy consists of an MSBuild extension and a Visual Studio Project template.


Download SPDeploy Now




Features

  • SharePoint Solution Package (WSP) Compilation
    SPDeploy examines your project files and creates a SharePoint Solution Package (WSP). The manifest.xml is dynamically created based on the files that exist in the project. If you add a file, that file is automatically added to the WSP and deployed to SharePoint.
    Benefits:
    1. Provides single distribution file that can be deployed via standard SharePoint tools (stsadm). This makes deploying to QA, Staging and Production trivial. Third party executed deployments are also made very simple.
    2. Since the WSP file is excercised during development, you can be assured that deploy is not an issue when deploying to other environments.


  • Remote Deployment & Developer Isolated Environments
    SPDeploy is able to remotely deploy the compiled WSP file to a SharePoint server. This is important for several reasons:
    1. All source code is housed and maintained via source control. All customization must be contained within the WSP file which means they are a part of your project which in turn can be committed to source control. This alleviates the need to migrate a SharePoint content database from development to QA to Production.
    2. Allows multiple developers to work on a single project and deploy to a separate and distinct environment avoiding clashes and breaking changes that typically occur when developers attempt to deploy customizations to a single SharePoint development environment.
    3. Deployment takes place from inside Visual Studio. SPDeploy allows you to push you WSP through the entire WSP lifecycle (Add, Deploy, Upgrade, Retract, Delete). This allows you to focus on writing code instead of the minutia of deploying your WSP.
    4. Most importantly SPDeploy puts you, the developer, in complete control of your customization and how they are deployed.

       image


    Information direct from the readme.txt:

    Overview
    ========

    SPDeploy extends msbuild to include targets that allow you to create a SharePoint solution file (.wsp) from a standard C# class library project inside Visual Studio 2005. In addition, SPDeploy allows you to deploy your newly compiled wsp file to a remote SharePoint server. This allows you to develop locally and deploy to SharePoint remotely.

    Benefits of wsp compilation and remote deployment:

    1. All source code is housed and maintained via source control. All customization must be contained within the wsp file which means they are a part of your C# project which in turn can be committed to source control. This alleviates the need to migrate a SharePoint content database from dev to QA to Production.

    2. Provides single distibution file that can be deployed via standard SharePoint tools (stsadm). This makes deploying to QA, Staging and Production trivial. Third party executed deployments are also made very simple.

    3. Allows multiple developers to work on a single project and deploy to a separate and distinct environment avoiding clashes and breaking changes that typically occur when developers attempt to deploy changes to a single SharePoint development environment.

    For more information on SharePoint Solution files see http://msdn2.microsoft.com/en-us/library/aa543214.aspx

     

    Installation
    ============

    1. Install the msbuild extension. Copy the SPDeploy folder located in this zip file to your MSBuild extensions directory. For a standard installation this directory is located at c:\program files\msbuild.  The msbuild extension directory is located at <program files>\msbuild where <program files> is your default program files directory on your system drive. The final path to the SPDeploy targets file should be <program files>\msbuild\spdepoy\v1\spdeploy.targets

    2. Install the Visual Studio Project Template. Copy the SPDeploy_Template.zip file to your Visual Studio project template directory located at <My Documents>\Visual Studio 2005\Templates\ProjectTemplates

     

    Creating a Project
    ===========================

    1. Inside Visual Studio, click file -> new -> project -> visual c# -> SPDeploy.

    2. Add user specific settings to SPDeploy.user. Open SPDeploy.user under the properties directory. Setup the WspServerName, WebApplicationUrl and WebApplicationOwnerEmail property values. These properties should contain the server name where the wsp is to be deployed and the url of your deployment web application.

    3. Setup manifest.xml GUID. Open the manifest.xml in the root directory of the project, and add a GUID to the SolutionId attribute. To create a guid go to tools -> create guid -> copy, then paste into the SolutionId attribute.

    4. Setup manifest.xml assembly. Inside the manifest.xml uncomment the assemblies element and add the name of your assembly to the Location attribute of the Assembly node. This will ensure your assembly is deployed to the GAC as a part of the wsp deployment.

    5. Strongly name your assembly. Right click on the project -> properties -> signing. Click "sign the assembly". Select a new key and give it a name.

    6. Build the project.

    7. Execute the CreateWebApplication target. See Executing SPDeploy below for more details.

    8. Execute the AddWsp target to add the wsp to your server.

    9. Execute the DeployWsp target. Your wsp file will now be deployed to the farm and all customization are available.

    10. Execute the CreateSite target. This will create a top-level site inside your web application.

     

    About SPDeploy.user
    ===================

    The SPDeploy.user file defines properties that are specific to your project and to the developers working on your project. These properties are consumed by the SPDeploy msbuild extension. At minimum you must override the WspServerName and WebApplicationUrl properties but you can override any property you like.

    You can also specify properties conditionally based on the executor's username. This allows for isolated and independant environments per developer.
    For example:

        <PropertyGroup Condition="$(USERNAME) == 'clints'">
            <WspServerName>dev-clints-wss</WspServerName>

            <WebApplicationUrl>http://$(WspServerName):88</WebApplicationUrl>
            <WebApplicationOwnerEmail>clints@ascentium.com</WebApplicationOwnerEmail>
        </PropertyGroup>

        <PropertyGroup Condition="$(USERNAME) == 'johnl'">
            <WspServerName>dev-johnl-wss</WspServerName>

            <WebApplicationUrl>http://$(WspServerName):81</WebApplicationUrl>
            <WebApplicationOwnerEmail>johnl@ascentium.com</WebApplicationOwnerEmail>
        </PropertyGroup>

     

    Executing SPDeploy
    ==================

    Supported Targets: ComileWsp, AddWsp, DeployWsp, UpgradeWsp, RetractWsp, DeleteWsp, UpgradeIncremental, UpgradeIncrementalFiles, UpgradeIncrementalAssembly, CreateSite, DeleteSite, CreateWebApplication, DeleteWebApplication

    You can execute these targets via the command prompt from the project directory like so:
    msbuild /target:<target name> <project name>.csproj

    Where <target name> is the msbuild target such as AddWsp or DeployWsp and <project name> is your project's name.

    To add visual studio support for launching these targets import the included SPDeploy.vssettings file into visual studio by going to tools -> import/export settings. This will add external tools items that are located under the tools menu in visual studio. You can also customize this further by creating a toolbar or menubar for these tool items.

    WARNING: Importing these settings with override all external tools settings inside visual studio.

     

    Remote Debugging
    =================
    Remote debugging can be problematic and somewhat tricky as most people have found. Here is the technique that works for me.

    1. Attach to the remote process. From your dev local machine inside Visual Studio go to Debug -> Attach to process. Change the qualifier field to your remote server name, pick ALL the w3wp.exe processes and click attach. At this point you may run into issues around firewalls, dcom, etc. See http://support.microsoft.com/kb/910448


    2. Ensure that your pdb symbols file has been loaded.

    Open the modules window while in debug mode to ensure that the symbols for your assembly are loaded. This window is notoriously hard to find, here's how I do it: Right click on a toolbar and go to customize. In the left pane choose debug, then drag the modules icon from the right pane onto a toolbar. Close the customize dialog and click on the module icon. You will now have the modules window open and you can scroll to find your assembly.

    If your assembly's symbols are not loaded, right click on the modules window and choose symbols settings. Now make sure that the spdeploy temporary folder is in the list, the spdeploy temporary folder is where spdeploy copies assemblies, PDBs and WSPs when deploying to your remote server. The temporary directory is located at \\<remote server name>\c$\_spdeploy\<project name>. Make sure this path is visible in the symbols setting dialog and that the checkbox next to it is checked. Now close the symbol settings dialog.

    In the modules window you should now see that the symbols for your assembly are loaded and your breakpoints are working.

     

    Team Build
    ==========

    Team Build is supported and wsp compilation works as normal. You will need to install the SPDeploy extensions on the Team Build server as descibed in this document. After a team build is executed, you should see the compiled wsp inside the output folder along with the assembly.

     

    NOTES
    =====

    - References to your project are compiled into WSP when the "Copy Local" attribute is turned on for the reference. To change this attribute, right click on your reference in visual studio and select properties. Then change the value of the "Copy Local" attribute.

    - Your development machine must be on the same domain as your SharePoint server.

    - makecab.exe must be in your DOS path for the wsp compilation to work. To make sure that makecab is in your path, open a command prompt and type makecab.exe

    - When loading an SPDeploy extended project you may receive a security warning stating that the project file has been customized. Choose "Load Project Normally". This is an issue that I am actively trying to resolve.

    - The SPDeploy project template is based on the ASP.net web application project type. You will need to have Visual Studio 2005 SP1 install so that this project type is available.

    Feedback
    ========

    If you have comments, suggestions or requests please email me at clints@ascentium.com

    Wednesday, June 27, 2007

    How To: Custom STSADM Commands

    With WSS 3.0 it is possible to create your very own stsadm commands. Yep, and it's relatively simple. I have been using this approach for tasks where I would normal create a standalone console application. Even better, commands can be deployed via wsp solution packages for single touch installation. That's for another post :) Here's a brief rundown on how to create a custom stsadm command.

    1. Create your command class. You must implement the Microsoft.SharePoint.StsAdmin.ISPStsadmCommand interface which consists of 2 methods, GetHelpMessage and Run. Inside the run method you have full control of the stsadm thread running in the end user's security context. You can perform any SharePoint API actions, connect to external databases, etc. The sky is the limit. Here's an example.
      public class SomeCommand : ISPStsadmCommand {

      public string GetHelpMessage(string command) {
      return "stsadm\n -o somecommand -url [<Url>]";
      }

      public int Run(string command, StringDictionary args, out string output) {
      // Do some stuff here with the SharePoint API
      return 0;
      }
      }

      The args parameter is a name value pair where the key is the command line option and the value is the argument to that options. For example: stsadm -o somecommand -url http://spsimon.blogspot.com  would yield url as a key and http://spsimon.blogspot.com as a value.

      The output parameter is a reference to a string that the frame work will write to the screen when the command is complete. If you have invalid input or an error occurs, you will need to se the output value accordingly.

    2. Register your command with STSADM. Now we need to tell stsadm.exe how to run our custom command. The xml stsadm configuration files are located in the 12\Config directory and each file begins with "stsadmcommands". To register our class we might create an xml file called stsadmcommands.somecommand.xml. Feel free to open the other xml command definition files in this directory. stsadm finds these files and associates the corresponding command name with your custom command class. The command assembly must be in the Global Assembly Cache. Here's an example.

      <command name="somcommand"
         class="Commands.SomeCommand, Commands, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a1df43608d33992f" />



    For a more verbose explanation see the official documentation.

    How To: Use SharePoint Explorer View in an ASP.NET page

    For any SharePoint document library there is a default view called "Explorer View". This view displays the the contents of the document library as Windows explorer window that can be used to drag/drop or copy/paste files. This is very handy when transferring large sets of files/folders to a SharePoint document library. 

    image

     Recently I implemented this view in a custom aspx page. Here's how I did it:

    1. Add the explorer view container to your page. The explorer view uses an iframe as the container for the windows explorer window. Here is what the markup should look like.
      <iframe id="frmFolder" width="600" height="400" name="frmFolder" 
      src="/_layouts/blank.htm" class="ms-httpFolder"></iframe>

    2. Initialized the explorer view container. Add the following code to your OnLoad page handler to initialize the iframe. In this example I am loading the root folder of the site collection. You can load any SharePoint folder you like.
      string js = string.Format(@"
      function navtoframe() {{ NavigateHttpFolderIfSupported('{0}{1}', 'frmFolder'); }}
      _spBodyOnLoadFunctionNames.push('navtoframe');"
      , SPContext.Current.Site.Url, SPContext.Current.Site.RootWeb.RootFolder.ServerRelativeUrl);

      Page.ClientScript.RegisterStartupScript(GetType(), "frmFolder", js, true);


    The NavigateHttpFolderIfSupported method is defined in the core.js file, which is a part of the SharePoint infrastructure and is associated with all SharePoint pages.

    Friday, June 22, 2007

    Using .NET Reflector as SharePoint documentation

    Here is something that has proven invaluable to me when trying to get a leg up as a consultant with the SharePoint platform. Use .NET Reflector to deduce how the SharePoint API is implemented.

    For the uninitiated, .NET Reflector is a free (and most excellent) tool written by Lutz Roeder that uses the reflection features built into the .NET framework to allow you to browse and even decompile any assembly. Yes! It's true you can open any assembly in reflector and see the source code. With only minimal trade-offs this allows you to see the author's implementation. In terms of SharePoint this means that you have a ton of examples at your fingertips. All you have to do is open the SharePoint assemblies and/or executables in Reflector to see how they are implemented. 

    There is one situation where this approach isn't viable. Assemblies can be "obfuscated" which means that all private methods/properties/fields are scrambled such that they are not readable by humans. In general this is a tactic used by commercial software companies to protect trade secrets. Thankfully (and I mean thank god!) the SharePoint team decided not to obfuscate their assemblies. There are minor exceptions, some security related code is obfuscated, but 99.9% of all SharePoint assemblies and executables can be decompiled.

    Let's face it, the SharePoint documentation is pretty crappy. Yes, there are some decent examples, but there are also lots of things like "SPWeb.Update() : Updates the web." This is not going to be helpful for people like us who are spelunking into the bowels of the SharePoint beast.

    image Here's an example. I recently had the need to update the farm credentials via the API (don't ask why, just go with me here.) Guess what, stsadm does this function from the command line and stsadm.exe can be decompiled.

    Open up Reflector. Drag in stsadm.exe, then navigate to the SPUpdateFarmCredentials operation. Bingo, here's how you update the farm credentials.

    Save an Outlook appointment from ASP.NET

    Outlook supports import/export operation via a file format called vCalendar. vCalendar files are standardized text files that contain all the information held inside an appointment. What’s important for us (as asp.net developers) is that outlook will open vCalendar files (.vcs) when signaled to do so from a web browser. But first things first, here’s the format for the vCalendar file.

    BEGIN:vCalendar
    VERSION:5.0
    PRODID:-//Microsoft Corporation//Works 2000//EN
    BEGIN:vEvent
    DTSTART:20011225T000000
    DTEND:20011226T000000
    SUMMARY;ENCODING=QUOTED-PRINTABLE:Christmas
    CATEGORIES;ENCODING=QUOTED-PRINTABLE:Gifts, Holiday
    PRIORITY: 3
    END:vEvent
    END:vCalendar

    You can also look at the .vcs format by exporting any outlook appointment by choosing file->save as and selecting the .vcs format. Now for the important part. Below is an example of how to write a vCalendar file from your asp.net application.

    string uid = Guid.NewGuid().ToString().Replace("-", "");
    string start = DateTime.Now.AddHours(1).ToString("yyyyMMddTHH0000");
    string end = DateTime.Now.AddHours(2).ToString("yyyyMMddTHH0000");

    this.Response.Clear();
    this.Response.Charset = "";
    this.Response.ContentType = "text/x-vCalendar";
    this.Response.AddHeader("Content-Disposition", "filename=Event.vcs;");
    string mask =
    "BEGIN:VCALENDAR\n"
    + "PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN\n"
    + "VERSION:1.0\n"
    + "BEGIN:VEVENT\n"
    + "DTSTART:{0}\n"
    + "DTEND:{1}\n"
    + "UID:{5}\n"
    + "SUMMARY;ENCODING=QUOTED-PRINTABLE:{2}\n"
    + "DESCRIPTION;ENCODING=QUOTED-PRINTABLE:{3}\n"
    + "LOCATION;ENCODING=QUOTED-PRINTABLE:{4}\n"
    + "PRIORITY:3\n"
    + "End:VEVENT\n"
    + "End:VCALENDAR";

    response.Write(string.Format(mask, start, end, subject, description, location, uid));
    response.End();

    First, we need to clear the response stream of any output that has already been buffered. Then set the ContentType to the MIME header associated with the vCalendar file (text/x-vCalendar). Setting the content-disposition header is also important because this is the filename that the user will see. Now we can begin to write the output. I have choosen to create a mask and fill it in with the various data elements for our appointment. The final step is to write reponse output and end the response. When this code executes, the user will see the standard “open or save” dialog inside internet explorer. If the user chooses to open the file, outlook will be launched and the appointment detail screen will be displayed with the information we have specified in the code. spiffy!

    Sunday, June 17, 2007

    WSS Web Template Provision Class

    WSS has a neat little feature that allows you to override the default site provisioning behavior as defined in the onet.xml file. When the WSS runtime detects that you have a provision class associated with a template, it ignores the provisioning definition in the onet.xml file and calls your custom provision class. This opens many possibilities for provisioning site in any way that you see fit. If you wanted to replace the default onet.xml provisioning, that is possible. Or if you want to apply another web template then provision files, list items, etc., no problem as well. Here's how it's done: 

    1. Create your provision class. This class will be called by the WSS framework when your template is instantiated. It must inherit from the Microsoft.SharePoint.SPWebProvisioningProvider class and implement the Provision(SPWebProvisioningProperties) method. As you might expect this method is called when the web is provisioned by the WSS framework.
      public class WebProvisionProvider : SPWebProvisioningProvider {
      public override void Provision(SPWebProvisioningProperties props) {
      SPWeb rweb = props.Web;
      rweb.ApplyWebTemplate(props.Data);
      ProvisioningManager pman = new ProvisioningManager();
      pman.ImportContent(rweb);
      pman.ImportChildWebs(rweb);
      }
      }

      In this example I am applying a web template as specified by the SPWebProvioningProperties.Data property. This property provides a mechanism to pass arbitrary data into your provisioning class. Here, I am using the data property to pass in the actual template I'd like to apply to the new web. After the template is applied I am calling my custom provisioning framework, which imports content (such as files, pages, list items) and also creates predefined child webs.

    2. Wire your provision class to your template. In this step we tell SharePoint to use our provisioning class when a specific site template is instantiated. For this you will need to create a webtemp.xml file (see the SDK if you haven't done this before). Add something similar to the below xml to your webtemp file. This tells SharePoint that you want to execute your custom provision class when this template is instantiated.
      <Configuration ID="100" Title="Blank Flex Site" Description="Blank Flex Site." ImageUrl="/_layouts/images/stsprev.png"    
      Hidden="FALSE" DisplayCategory="Publishing"
      ProvisionAssembly="Flex, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a1df43608d33992f"
      ProvisionClass="Flex.Provision.Providers.WebProvisionProvider"
      ProvisionData="flex#0"
      />

    Saturday, June 16, 2007

    Hello World

    Hi, my name is Clint and I am a software consultant working at Ascentium in Bellevue, Washington. I hope to use this blog to share my experiences with Windows SharePoint Service (WSS) and Microsoft Office SharePoint Server (MOSS). I've been working with SharePoint for roughly 5 years on systems ranging from simple content management to public presence sites to line of business applications, all based on the SharePoint platform. Primarily I am a custom application developer who works with clients to leverage the SharePoint platform for their business needs.

    For anyone with experience with SharePoint consulting you know that SharePoint can be a beast when it comes to custom application development. There are many many tricks/hacks I have discovered (and invented) over the years and I think it's time to share those. You see, I have been leaching other blogs for a long, long time and my conscience has finally caught up to me. So here I am blogging in the hopes that I can contribute to the community.