Saturday 31 March 2012

Embedding Dynamic PowerPoint Decks into SharePoint 2010 page

Few hours to Earth Hour (31st March 2012, 8.30-9.30 PM) now and atmosphere has really gone cloudy and filled with pleasant smell which happens on first rain. I will take this as Mother Nature’s appreciation for Earth Hour :-)

In last week, I was trying to add PowerPoint decks to the web page in SharePoint which satisfies some criteria. There were two approaches to do so.
1.       Use Fast Search PowerPoint Preview functionality
2.       Use Office Web Apps to display the PowerPoint Deck using Page Viewer webpart or in iframe

I chose later approach as I felt it easy and quick. So I uploaded the PowerPoint Deck to a document library. Opened it using office web apps. Copied the url into clipboard. Then I added Page Viewer web part to web part page and set the url property of it to the PowerPoint Deck url in clipboard. Easy it is…but surprisingly Page Viewer web part gave an error…“This content cannot be displayed in a iframe”.

After little bit of googling, I came to know the reason. Steven Van de Craen in his blog has thrown a light on it. Excel services and Office Web Apps pages render the “X-FRAME-OPTIONS:SAMEORIGIN” response header which causes this error. Steven has also given a solution to it in his Codeplex project which removes this culprit header using HttpModule.

Even though Steven’s solution is lucrative and easy, I was not able to use it because of two reasons….
1.       It opens a gateway to Clickjacking attacks
2.       It uses HttpModule which is not allowed in the SharePoint Governance at my client place due to many valid reasons.  

Further googling, I found another blog by Sean Earp which uses the PowerPointFrame.aspx instead of PowerPoint.aspx. But unlike PowerPoint.aspx which accepts the PowerPoint deck url as query string, PowerPointFrame.aspx uses some guid which somehow points to the PowerPoint deck.

Figure 1 : PowerPoint.aspx and PowerPointFrame.aspx urls


Hence this solution is good only when your PowerPoint deck to be displayed on a page is fixed. In my case however, it was dynamic. I wanted to embed the PowerPoint deck which satisfies some criteria. So after little bit of digging, I was able to figure it out how PowerPointFrame.aspx query string guid is constructed. Here is the code snippet I used to generate the guid.


//listItem represents PowerPoint deck item in doc lib
string powerpointGuid = string.Format("F{0}m{1}m{2}m", SPContext.Current.Site.ID.ToString("N"),
SPContext.Current.Web.ID.ToString("N"),
listItem.UniqueId.ToString("N"));


Here is how my custom web part looks with PowerPoint decks. It displays the decks in iframes.

Figure 2 : My PowerPoint Deck custom webpart


You also can use this logic but beware that Microsoft can change this guid format in any of its SharePoint updates. If you are using it, don't forget to add popout=1 into the PowerPointFrame.aspx's query string. If you figure it out what is SlideId query string parameter, then please ensure that you are telling me that first.

Earth Hour has been started…Need to start mine one now

Saturday 24 March 2012

Basics of SharePoint Features

SharePoint feature acts as container of the functionalities or customizations. These functionalities/ customizations are created either by declaratively or programmatically and can be made available or unavailable by activating or deactivating the feature.

SharePoint feature has following scopes
1.       Farm - feature contained functionality becomes available to whole farm
2.       Webapplication - feature contained functionality becomes available to the web application in which feature is activated
3.       Site  - feature contained functionality becomes available to the site collection in which feature is activated
4.       Web - feature contained functionality becomes available to the sub-site in which feature is activated

The SharePoint feature contains following type of files
1.       Feature manifest - Feature.xml
2.       Element manifest - Element.xml
3.       Element File - any other file than feature.xml and element.xml. It can be .master, .webpart, .aspx, resx, .css etc file.

Feature Manifest
The below figure shows the attributes of feature manifest file apart from UpgradeSolutions.

Figure 1 : Feature Manifest


Element Manifest
Element manifest file contains CAML definitions of various SharePoint content elements. Some of them are mentioned below.

Figure 2 : Element Manifest


Following table lists down the content elements with their purposes.

Content Element Tag
Purpose
<ContentType />
Creates content type
<ContentTypeBinding />
Binds the content type with list instance
<Control />
Adds control into the delegate controls present in master page
<CustomAction />

  • Create link at predefined SharePoint locations
  • Create buttons in ribbon
<CustomActionGroup />
Creates group for custom actions
<FeatureSiteTemplateAssociation />
Staples the features to existing site definitions
<Field />
Creates Site column
<HideCustomAction />
Hides the existing custom actions link
<ListInstance />
Creates the list instance
<ListTemplate />

Creates custom list definitions
<Module />
Uploads any file to SharePoint document libraries
<PropertyBag />
Creates property in the property bag collection for the list item, file, folder, or website
<Receivers />
Registers the SharePoint list related event
<WebTemplate />
Create feature based site template
<Workflow />
Defines custom workflow schema
<WorkflowActions />
Defines custom workflow activity
<WorkflowAssociation />
Associates the workflow with list/ content types


Few Design level considerations

1.       Feature must be scoped at appropriate level so that feature functionality is only available in desired places.
2.       In case there are multiple functionalities that need to be added through the feature, then it is better to have separate feature for each one of them so that you can individually turn on/off those through feature activation/deactivation. This will ensure one functionality is not affecting other.
e.g. Consider a feature which adds a web part to the page layout using AllUserWebpart element. Activating this feature multiple times results in multiple web part addition to the page layout. Hence if you are adding a new functionality in the same feature, you will need to deactivate/ activate it. This in turn will result in multiple web parts addition to the page layout which is undesired.
3.       The feature can be made configurable using the feature property bag collection
e.g. Suppose you want to create a feature which applies your custom master page to the site in feature receiver code. Then it is better to add a property to the feature as shown in below figure.

Figure 3 : Feature Properties

This property then can be used to apply the master page in feature receiver code

Figure 4 : Feature Receiver

4.       Feature dependencies and hidden features
Feature dependencies always work in hierarchical manner. If you make the parent feature hidden then it will get auto activated on dependent feature activation.Good read about feature dependencies here.


Feature Receiver
Feature supports following events.

    
    public class DemoEventReceiver : SPFeatureReceiver
    {
        //Raised after a feature has been activated.
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
        }

        // Raised before a feature is deactivated.
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
        }

        // Raised after a feature has been installed.
        public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
        }

        // Raised before a feature is uninstalled.
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
        }

        // Raised when a feature is upgrading. This event is newly added in SharePoint 2010 
        public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, System.Collections.Generic.IDictionary<stringstring> parameters)
        {
        }
    }




Deployment challenges

Following are the feature operations not supported in solution upgrade (stsadm upgradesolution command). 
  • Removing old Features in a new version of a solution.
  • Adding new Features in a solution upgrade.
  • Updating or changing the receiver assembly for existing Features in a new version of a solution.
  • Adding or changing Feature elements (Element.xml files) in a new version of a solution.
  • Adding or changing Feature properties in a new version of a solution.
  • Changing the ID or scope of old Features in a new version of a solution.
  • Removing Feature elements (Element.xml files) in a new version of a solution.
  • Removing Feature properties in a new version of a solution.

One can say, a simple workaround for overcoming these challenges is to retract, delete and redeploy the wsp package. But beware, there is a catch! You should before deactivate/ uninstall the features which are supposed to be removed or whose feature Ids are to be changed. Otherwise, it leaves dangling feature entries in SharePoint content database, which will give troubles especially when you are upgrading from MOSS 2007 to SharePoint 2010. You can find such dangling features either by running the stsadm preupgradecheck command or closely monitoring the SharePoint ULS logs.

Now SharePoint 2010 has brought a very interesting functionality called Feature Upgrade which comes into picture when stsadm upgradesolution command is used. It addresses some of the challenges mentioned above. Nobody explains this functionality better than The Chris Obrien in his FeatureUpgrade blog series.

The SharePoint features can be forcefully activated through stsadm/ powsershell scripts. But you should not use it unless it is must.


God…you there? ….are my prayers loud enough?

Friday 23 March 2012

MP.SPDevExt VSIX for SharePoint 2010 : Sandbox Workflow Action

MP.SPDevExt Blog Series:
Part 1: MP.SPDevExt VSIX for SharePoint 2010
Part 2: MP.SPDevExt VSIX for SharePoint 2010 : Custom Field Type
Part 3: MP.SPDevExt VSIX for SharePoint 2010 : Sandbox Workflow Action (Currently reading)
Part 4: MP.SPDevExt VSIX for SharePoint 2010 : Sandbox Solution Validator
 

If you are frequently using the SPD workflows, then at times, you must have felt need of having ability to execute your custom C#/ VB.net code as no OOB workflow actions is sufficing to your requirement. Yes, this is possible! There are two ways to do that…

   a.      By creating Custom Workflow Activity (Farm solution)
   b.      By creating the Sandbox workflow action (Sandbox solution)


Let us see how MP.SPDevExt helps to create your own Sandbox Workflow Action…

1.       Download and install the MP.SPDevExt Visual Studio extensions.
2.      In Visual Studio 2010, click New Project, expand the SharePoint node, click 2010, and then click Empty SharePoint Project. Name the project as MP.SPDevExtSandBoxDemo and then click OK.
3.       In the SharePoint Customization Wizard, select the local SharePoint site that can be used for debugging and  Deploy as a Sandbox solution option shown in Figure 1. Click Finish.

Figure 1 : Select Deployment Method

4.       Go to Solution Explorer and right click on MP. SPDevExtSandBoxDemo project and click on Add=>New Item

Figure 2 : Add New Item


5.       In Add New Item window, select the SharePoint =>2010 tab in left menu. Then click on Sandbox Workflow Action (MP.SPDevExt) item template, type CreateList in Name textbox and click on Add button.

Figure 3 : Create Sandbox Workflow Action project item

6.       This will create a CreateList project item as shown in figure 4.

Figure 4 : CreateList project item


The below table shows the files generated and their usages. By default, Custom Field Validator contains code for creating the list.

File Name
Usage
Elements.xml
This contains the workflow action definition
CreateList.cs
This class file containing the code to be executed.

7.       Below are the default code generated

Elements.xml:

<?xml version="1.0" encoding="utf-8"?>
<Elements Id="1bf597a5-0b7c-4b15-ac3b-8a87325c87c4" xmlns="http://schemas.microsoft.com/sharepoint/">
  <WorkflowActions>
    <Action Name="Custom Workflow Action - CreateList"
        SandboxedFunction="true"
        Assembly="$SharePoint.Project.AssemblyFullName$"
        ClassName="MP.SPDevExtSandBoxDemo.CreateListWorkflowAction"
        FunctionName="CreateList"
        AppliesTo="all"
        Category="SandBox Workflow Actions">
      <RuleDesigner Sentence="Create List named %1 (select Advanced Properties to configure additional settings).">
        <FieldBind Id="1" Field="listName" DesignerType="Text" Text="this list"/>
      </RuleDesigner>
      <Parameters>
        <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In" DesignerType="Hide" />
        <Parameter Name="listName" Type="System.String, mscorlib" Direction="In" DesignerType="TextBox" Description="Name of the list" />
        <Parameter Name="description" Type="System.String, mscorlib" Direction="In" DesignerType="TextArea" Description="Description of the list"/>
        <Parameter Name="listTemplate" Type="System.String, mscorlib" Direction="In" DesignerType="TextBox" InitialValue="Custom List" Description="Name of the list template"/>
        <Parameter Name="documentTemplate" Type="System.String, mscorlib" Direction="In" DesignerType="TextBox" Description="Name of the document template if list to be created is of type Document Library" InitialValue=""/>
        <Parameter Name="Result" Type="System.String, mscorlib" Direction="Out" DesignerType="ParameterNames" Description="Result of activity : Success, Failure"/>
      </Parameters>
    </Action>
  </WorkflowActions>
</Elements>


CreateList.cs

using System;
using System.Collections;
using Microsoft.SharePoint;
using Microsoft.SharePoint.UserCode;
using Microsoft.SharePoint.Workflow;
 
namespace MP.SPDevExtSandBoxDemo
{
    public class CreateListWorkflowAction
    {
        public static Hashtable CreateList(SPUserCodeWorkflowContext context, string listName, string description, string listTemplate, string documentTemplate)
        {
            SPWorkflow currentWorkflow;
            SPListTemplate currentListTemplate;
            SPDocTemplate currentDocumentTemplate = null;
 
            listName = listName.Trim();
            description = description.Trim();
            listTemplate = listTemplate.Trim();
            documentTemplate = documentTemplate.Trim();
 
            using (SPSite site = new SPSite(context.SiteUrl))
            {
                using (SPWeb web = site.OpenWeb(context.WebUrl))
                {
                    Hashtable result = new Hashtable();
                    result["Result"] = "Failure";
 
                    //Get the current workflow instance
                    //If this is List Workflow
                    if (context.ListId != Guid.Empty)
                    {
                        SPList list = web.Lists[context.ListId];
                        SPListItem item = list.GetItemById(context.ItemId);
                        currentWorkflow = new SPWorkflow(item, context.WorkflowInstanceId);
                    }
                    else //this is Site Workflow
                    {
                        currentWorkflow = new SPWorkflow(web, context.WorkflowInstanceId);
                    }
 
                    //Get the list template
                    if (string.IsNullOrEmpty(listTemplate))
                    {
                        currentListTemplate = web.ListTemplates["Custom List"];
                    }
                    else
                    {
                        try
                        {
                            currentListTemplate = web.ListTemplates[listTemplate];
                        }
                        catch
                        {
                            currentWorkflow.CreateHistoryEvent(101, nullnull""string.Format("Could not find the '{0}' list template", listTemplate), null);
                            throw new SPException("Failed to create list");
                        }
                    }
 
                    //Get the document template only if the list to be created is a document library
                    if (currentListTemplate.Type == SPListTemplateType.DocumentLibrary)
                    {
                        foreach (SPDocTemplate docTemplate in web.DocTemplates)
                        {
                            if (docTemplate.Name.Equals(documentTemplate))
                            {
                                currentDocumentTemplate = docTemplate;
                                break;
                            }
                        }
 
                        if (currentDocumentTemplate == null)
                        {
                            currentWorkflow.CreateHistoryEvent(101, nullnull""string.Format("Could not find the '{0}' document template", documentTemplate), null);
                            throw new SPException("Failed to create list");
                        }
                    }
 
                    if (currentDocumentTemplate == null)
                    {
                        web.Lists.Add(listName, description, currentListTemplate);
                        result["Result"] = "Success";
                    }
                    else
                    {
                        web.Lists.Add(listName, description, currentListTemplate, currentDocumentTemplate);
                        result["Result"] = "Success";
                    }
 
                    return result;
                }
            }
        }
    }
}
 


8.       Right click on SPDevExtSandBoxDemo project and click on Deploy menu as shown in figure 5. Visual studio will package and upload/activate the Sandbox solution to the Solutions gallery.
Figure 5 : Deploy Solution

9.       In SharePoint Designer 2010, open the site collection in which SPDevExtSandBoxDemo is deployed.
10.   Click on Workflows in left hand navigation menu and then click on Site Workflows button in Ribbon. It will open the Create Site Workflow window. Type TestCreateListAction in name textbox as shown in figure 6 and click OK button.

Figure 6 : Create Site Workflow


11.   Click on Action button in Ribbon. It will open all the Actions available. Select the Custom Workflow Action – CreateList as shown in figure 7.

Figure 7 : Add CreateList workflow action to the step



12.   Go to the Advanced Properties as shown in figure 8.

Figure 8 : Go to the Advanced Properties



13.   Set the values of the properties as shown in figure 9.

Figure 9 : Set the CreateList action properties


14.   Click on Publish button in Ribbon to publish the workflow.

Figure 10 : Publish workflow


15.   Navigate to the SharePoint site in which workflow is published. Click on Site Actions=>View All Site Content menu.

Figure 11 : View All Site Content menu

16.   Click on Site Workflows link button.

Figure 12 : Site Workflows button

17.   Click on the TestCreateListAction workflow link. Then in next window, click on Start button to execute the workflow.

Figure 13 : Start TestCreateListAction workflow


18.   Once workflow execution is finished, go to Site Actions=>View All Site Content page. Notice that Demo List is created by the CreateList workflow action.

Figure 14 : Demo List in View All Site Content page


The Sandbox Workflow Action item template is still under the development. It adds code to create the list by default as a sample to start with. The next version of MP.SPDevExt will have WPF based Item Template wizard in which Workflow Action name, description, function name, parameters etc can be entered. So keep an eye on next version. It will contain  many other enhancements that you will love to have. :-)

BTW, I rejected two 1+ year travel opportunities (Switzerland and US) in last two weeks in hope that India will offer me what I want. Too late...heading home now…