Wednesday, October 28, 2009

SharePoint 101 - Dispose of SPWeb Objects

One of the most important things you can do to ensure a stable and responsive SharePoint environment is to dispose of SPWeb objects. SPWeb object are fat. The "automatic" garbage collection in .net 2.0 just doesn't do the job. So, use a using statement or explicitly call dispose. If you are passing SPWeb objects around you need to dispose of them in the method were they are used. The only time you'll run into issues disposing of an SPWeb object is when it's the current web like SPWeb myWeb = SPContext.Current.Web. Go ahead and try disposing of it anyway, it's better to fix the error and make sure you're disposing properly. A tool is available to help you check your code, SPDisposeCheck.

SPWeb myWeb = site.OpenWeb();
try{}
catch{}
finally
{
    if(myWeb != null)
      myWeb.Dispose();
}


using(SPWeb myWeb = site.OpenWeb())
{}

Saturday, October 24, 2009

Allow Anonymous Users to run SharePoint Workflows

The code that follows will allow you to start a workflow from an event receiver. There are a couple of reasons that you may want to do this. First, you have more control over your workflow. You can check and make sure that it's started successfully. Second, you can allow users or processes to execute workflows that couldn't otherwise. Next is how you can find your workflow's guid.

SharePoint Designer Workflow - 650F1ED3-2FEE-499D-A9A2-40BE326951C1

OOB Workflow - get the TemplateID from the url
the guid here is
19149E7D-1970-4432-B174-D88C7BF2B4008


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Net;
using System.Diagnostics;
using Microsoft.SharePoint.Workflow;

namespace StartWorkflow
{
    class StartWorkflowOnItemAdded : SPItemEventReceiver
    {
       // use the itemadded event to start a workflow
       public override void ItemAdded(SPItemEventProperties properties)
       {
               try
               {
                  // make sure no other events can be fired
                  DisableEventFiring();

                  // execute StartWorkflow as application pool
                  SPSecurity.RunWithElevatedPrivileges(delegate(){
                        StartWorkflow(properties);
                  });
              }
              catch (Exception ex)
              {}
              finally
             {
                 // allow other events to fire
                 EnableEventFiring();
             }
      }

      private void StartWorkflow(SPItemEventProperties properties)
      {
            using (SPSite site = new SPSite(properties.WebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                     try
                    {
                         SPList list = web.Lists[properties.ListId];

                         //enter your workflow Guid
                         Guid wGuid = new Guid("{}");
  
                         //get the association data
                         SPWorkflowAssociation wTemplate =
                         list.WorkflowAssociations.GetAssociationByBaseID(wGuid);

                         //start your workflow
                         site.WorkflowManager.StartWorkflow(properties.ListItem,
                         wTemplate, wTemplate.AssociationData);
                    }
                    catch (Exception ex)
                    {}
                }
            }
      }

    }
}

Thursday, October 22, 2009

SharePoint Progamming 101 - Impersonation

Many times you want to make sure that your code runs no mater what permissions the current user has. There are two easy ways that you can accomplish this in SharePoint. Both of the techniques do the same thing, execute code with the permissions of the application pool the site is running under.  You must create a new spweb object from a url after impersonating the application pool.

Method 1
            //Impersonate the pool
            WindowsImpersonationContext ctx = null;
            ctx = WindowsIdentity.Impersonate(System.IntPtr.Zero);          

            SPSite site = new SPSite("http://mysteurl");
            SPWeb web = site.OpenWeb();
            try
            {
                //All of this code will run with elevated privileges   

            }
            catch (Exception ex)
            {}
            finally
            {
                    web.Dispose();
                    //make sure you revert to normal
                    ctx.Undo();
             }

Method 2
            //Impersonate the pool
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite("http://mysteurl"))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                         //All of this code will run with elevated privileges     
                    }
                }
           });

Sunday, October 18, 2009

SharePoint Cross Site List Views with jQuery

Displaying or sharing lists across webs or site collections is not supported out of the box by SharePoint. The best way to overcome this limitation is by loading the list dynamically. Ok, sounds great but how am I supposed to do that? No worries, it's really pretty easy. I will use jQuery's load function to do the bulk of the work. The code here has been adapted from Christophe's solution . There are a number of changes, the most important being enabling sorting by column.

Load the jQuery library
I'm adding a content editor web part to include the jQuery library. You should probably add the library to your master page. Once you start using it you'll want to add it all over the place. Add a CEWP to your page and add the following script tag. This will load jQuery from Google.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>

Add the Script to the page
Add a new CEWP, yes you have to, to your page and add the following script.

<DIV id="ListPlaceholder"><span><b>...Loading...</b></span></DIV>

<script type="text/javascript">
// Make sure the page has been loaded
$(document).ready(function() {

    // Paste the URL of the source list below:
    var SelectedView = "http://test/sites/test/Lists/Tasks/AllItems.aspx";

    // Paste the URL of Add new items page
    var AddNewItems = "http://test/sites/test/Lists/Tasks/NewForm.aspx" + "?Source="+location.href;

    // Title
    var mTitle = "My List Name";

    // Title tool tip
    var MouseOverHeader = "My List Name Description";

    var innerHTML = "<a href=" + AddNewItems + " tabindex='0' accesskey='W'><nobr><span>" + mTitle + "</span><span id='WebPartCaptionWPQ6'/></nobr></a>";

    // Load the list
    $("#ListPlaceholder").load(SelectedView+" [id^='WebPartWPQ'] .ms-listviewtable", function(){processView();setColumnClicks();});

    // Format the returned list view
    function processView(){

        //set the header of the content place holder
        var hdrNode = $("#ListPlaceholder").closest('tr').prev().find('h3');
        hdrNode.html(innerHTML);
        hdrNode.parent(0).attr('title', MouseOverHeader);

        //Remove the dropdown menus for items
        $("#ListPlaceholder *").removeAttr("id").removeAttr("onclick").removeAttr("onfocus").removeAttr("onmouseover");
    
//Format the Items
        $("#ListPlaceholder a").each(function() {
            if ($(this).attr("href").indexOf("javascript") == -1){
                if ($(this).attr("href").indexOf("mailto") == -1){
                    if ($(this).attr("href").indexOf("?") > 0){
                       $(this).attr("href",$(this).attr("href")+"\&Source="+location.href);}
                    else {$(this).attr("href", $(this).attr("href")+"?Source="+location.href);}
                }
            }
        });// close each
    }// close processView

//Make the titles sortable
    function setColumnClicks(){
        $("#ListPlaceholder a[sortingfields]").click(function(){
            var srtFields = $(this).attr('sortingfields');
            $("#ListPlaceholder").load(SelectedView +"?" + srtFields + "  [id^='WebPartWPQ'] .ms-listviewtable", function(){processView();setColumnClicks();});
        });   
    }

});//Close document.ready
</script>

Explaining the script
This line in the script really does all of the work

$("#ListPlaceholder").load(SelectedView+" [id^='WebPartWPQ'] .ms-listviewtable", function(){processView();setColumnClicks();});

This line means the following. Find the element on this page with an id of ListPlaceholder and load the content from SelectedView. Only load the content where the element id starts with WebPartWPQ and has the class ms-listviewtable. After loading the content run functions processView() and setColumnClicks().
Another question you may have is what is this Source used all over the place for ? SharePoint uses Source= in the url to return to the previous page.

Sunday, October 11, 2009

Customizing a SharePoint Banner

Modifying the header or banner portion of a SharePoint page is not as easy as you would hope. In this post I’ll walk you through one possible approach to adding a custom look while maintaining the functionality and positioning of out of the box components. I’m going to be using SharePoint designer to modify the master page to customize the banner. This is what I’m starting with.


Open the site you’re working on with SharePoint designer File > Edit with Microsoft Office SharePoint Designer. COPY the default.master page found in this directory _catalogs/masterpage/. Open the copied master page, find the tag below, it’s the starting point for the header portion of the page.

<TABLE class="ms-main" CELLPADDING=0 CELLSPACING=0 BORDER=0 WIDTH="100%" HEIGHT="100%">

Add this line directly underneath the table tag. You can also add an image as the background here as long as it grows and shrinks gracefully.

<tr><td><div style="background-color:fuchsia"><table width="100%">

Find <asp:ContentPlaceHolder ID="WSSDesignConsole" runat="server"> close the tags you’ve just added by adding this line above the content placeholder </table></div></td></tr>.

Find and remove class="ms-globalTitleArea".

Find and remove class="ms-bannerContainer".

Ok, we’ve changed the background color. Let’s change the title of the site. Find the content place holder tag below and remove it.

<td class="ms-sitetitle" width=100%>

    <asp:ContentPlaceHolder id="PlaceHolderSiteName" runat="server">
          <SharePoint:SPLinkButton runat="server" NavigateUrl="~site/" id="onetidProjectPropertyTitle">
         <SharePoint:ProjectProperty Property="Title" runat="server" /> </SharePoint:SPLinkButton>
    </asp:ContentPlaceHolder>
</td>

Once you have removed the content place holder tag and its contents you, will not be able to change the title via the admin settings in the browser. Add whatever markup here you would like.
 <h2 style="color:white">This has been way harder than it should be</h2>

You can use this area to include an image as well, replace the <h2> tag with something like this:
<div style="background-image:url('images/header.gif');background-repeat:no-repeat; height:150px"></div>

To Get rid of the icon to the left find the SharePoint:SiteLogoImage tag and replace LogoImageUrl="/_layouts/images/titlegraphic.gif" with LogoImageUrl="/_layouts/images/blank.gif"

 If you would like to change the color of the links in the header area you will need to override the following CSS classes, ms-SPLink, ms-globallinks and ms-sitemapdirectional.