Extending SharePoint Web Part


SharePoint provides a lot of different out-of-the-box Web Parts. But sometimes we wish a particular web part behaved in a little different way, or have some small extra functionality. Or we may want to implement a heavily modified Web Part, based on already existing one.

Today, I will show you how to extend the functionality of an already existing web part.

We will take for example the SharePoint RSS Viewer. It loads an RSS feed and visualizes it in a web part on a SharePoint site. One of its visible properties in the UI is the Feed URL. It’s a static string and doesn’t allow any kind of dynamic parameters. Example:


Those placeholders in my example are fictional, but you should get the idea of a dynamic value passed as a parameter to the service.

Our goal is to override the default behavior of the Feed URL field. We want to get to a point where we actually control how the Feed URL is being interpreted. We may want to replace placeholders with actual values, or do any other kind of adjustment of the URL, based on current and dynamic conditions. So here is the plan: we create a new web part, inherit the RSS Viewer web part and extend the Feed URL property.

Let’s start. Here is how the RSS View part settings look like in edit mode:

See the RSS Feed URL field? That’s what we are interested in.

Ok, start Visual Studio 2008 with the VSeWSS plugin installed. From the menu, start a new SharePoint -> Web Part project.

Give it a full trust

Change your namespace to something you prefer (for example, I will change it to HristoYankov). Rename the folder ‘WebPart1’ to ‘RSSAggregatorWebPartDynamicParam’. It will rename all classes and files for you. In the Solution Explorer, expand Properties and open AssemblyInfo.cs. Add this to the bottom:
[assembly: AllowPartiallyTrustedCallers]

and this to the top:
using System.Security;

Your Solution Explorer should look similar to:

Right click on your project, click on Properties, go to the Debug tab and set the SharePoint site URL you will be deploying to.

Open the RSSAggregatorWebPartDynamicParam.webpart file and set Title and Description to whatever you like.

Now, after your project is setup, starts the interesting part. Let’s inherit the RSS View control! What you need to do is…

Add this file as a reference – C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\microsoft.sharepoint.portal.dll. This is where the RSS View class is defined.

Open your RSSAggregatorWebPartDynamicParam.cs file and change your class from this:
public class RSSAggregatorWebPartDynamicParam : System.Web.UI.WebControls.WebParts.WebPart

to that:
public class RSSAggregatorWebPartDynamicParam : RSSAggregatorWebPart

And add this statement:
using Microsoft.SharePoint.Portal.WebControls;

Basically, we no longer inherit the basic WebPart class. Instead, we now inherit the whole functionality of the RSS Viewer web part and all of its user interface!

You can also delete this function, as we are not going to do any changes there.
protected override void CreateChildControls()

So far so good. Now let’s override the Feed URL property! Add this to your class:

[DefaultValue(""),Resources("RSSWebpart_Property_FeedUrl_Text","RSSWebpart_ToolPart_Group","RSSWebpart_Property_FeedUrl_Description"),Personalizable(PersonalizationScope.Shared),WebBrowsable(true)]public new string FeedUrl{get{return base.FeedUrl;}set{base.FeedUrl = value;}}

And add this statement:
using System.ComponentModel;

Three things to note here:
1. The FeedUrl property is the one which handles the URL which points to the RSS feed.
2. Note the attribute on top of the property. If you don’t add it, it won’t be visible in the UI
3. Note the ‘new’ keyword. This way we hide the underlying base FeedUrl property.

Ok, now we have control over the Feed URL. What we should do, is change the way FeedUrl is being returned. Change your get to look like:

get{return OverrideURL(base.FeedUrl);}

And create this function:

private string OverrideURL(string url){// TODO: Process the URLreturn url;}

So, in OverrideURL we can change any way we like the URL that is set in the User Interface and then return it modified. At this point, it is up to you how to utilize this capability.

For the purpose of the example, let’s just look for the string #current_date# in the URL and replace it with the current date.

private string OverrideURL(string url){return url.Replace("#current_date#", DateTime.Now.ToShortDateString();}

At the end, your code should look like:

using System;using System.Runtime.InteropServices;using System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint.WebPartPages;using Microsoft.SharePoint.Portal.WebControls;using System.ComponentModel;

namespace HristoYankov{[Guid("03badfa9-53e4-401a-bc60-28db88b202ac")]public class RSSAggregatorWebPartDynamicParam : RSSAggregatorWebPart{   public RSSAggregatorWebPartDynamicParam()   {   }

   [DefaultValue(""), Resources("RSSWebpart_Property_FeedUrl_Text", "RSSWebpart_ToolPart_Group", "RSSWebpart_Property_FeedUrl_Description"), Personalizable(PersonalizationScope.Shared), WebBrowsable(true)]   public new string FeedUrl   {       get       {           return OverrideURL(base.FeedUrl);       }       set       {           base.FeedUrl = value;       }   }

   private string OverrideURL(string url)   {       return url.Replace("#current_date#", DateTime.Now.ToShortDateString());   }}}

Now, right click your project and do ‘Deploy’. It should add a RSSAggregatorWebPartDynamicParam assembly to your GAC. When you go to your SharePoint site and do ‘Edit Page’ -> ‘Add Web Part’

you should be able to see your newly created web part listed in the pop up. Add it to the page. Put it in Edit Settings mode and set the Feed Url to something like:

It will be replaced by:
http://servername/service/Param1=06/06/2009 (for example)

NOTE: I just noticed this – as soon as you set the URL which contains the ‘placeholder’, the web part which is still in edit mode starts showing it with a replaced value (the date). I believe that the underlying value is still http://servername/service/Param1=#current_date#. So perhaps, in the method OverrideURL we should be taking into consideration if the web part is in edit mode. And if it is – just return the original parameter that was passed. Something like:

private string OverrideURL(string url){if (this.NotInEditMode){// Not in edit mode, perform changesreturn url.Replace("#current_date#", DateTime.Now.ToShortDateString());}else{// We are in edit mode, return URL as isreturn url;}}

I will be checking this later. As usual, you can get the full source code here.

But basically that’s it. With minimum amount of code and effort, we have extended the functionality of an already existing web part, to something that serves our concrete needs.

Hope this was helpful!


SharePoint Solution Hello World Tutorial


In this post, I will explain to you how to create your first SharePoint 2007 ASP.NET application. We will cover what tools you would need and what are the steps to creating a SharePoint Hello World app! I will keep the article short and clear on the necessary steps to be followed.

First, let’s start with the tools. You will need Visual Studio 2008 SP1 (preferably) this freely available plugin for it Visual Studio 2008 extensions for Windows SharePoint Services 3.0. It is a toolset for developing custom SharePoint applications: Visual Studio project templates for Web Parts, site definitions, and list definitions; and a stand-alone utility program, the SharePoint Solution Generator.

We assume that you have setup and you do have available a SharePoint site, on which we will be deploying our solution.

So, start your Visual Studio 2008. From the menu choose to start a new Project.

Visual Studio will show this window. Select GAC and proceed.

Your Solution Explorer would look like this, initially:

We need to create a couple of folders. Recreate the folder structure described in the screenshot below:

Right click on HelloSharePointWorld and select add new item. Select text file, but name it “Hello.aspx”

Put the following content in your newly created ASPX page.

<%@ Page Language="c#"Inherits="Hello, SharePointHelloWorld, Version=,Culture=neutral, PublicKeyToken=3cf44204a7ad1f1e"MasterPageFile="~/_layouts/application.master"%>

<asp:Content ID="Content1"ContentPlaceHolderID="PlaceHolderMain"runat="server"><asp:Label ID="lblLabel1" runat="server" Text="Hello World!"></asp:Label></asp:Content>

Note the PublicKeyToken value. It will be changed later. Now, create a new class in the App_Code folder. Name it Hello.aspx.cs. Your directory structure should look like:

The content of your Hello.aspx.cs should read:

using System;using Microsoft.SharePoint.WebControls;

public partial class Hello : LayoutsPageBase{ protected void Page_Load(object sender, EventArgs e) { }}

Right click on your project, select Properties from the menu. Go to the Debug tab and set the hostname and port number of the SharePoint site where you want your project to be deployed.

Then save.

Now, right click on your project in Solution Explorer and rebuild it. Then right click on it and Deploy.

Now, open Windows Explorer and navigate to C:\WINDOWS\assembly. You should see an assembly called SharePointHelloWorld there. Right click it and select Properties.

Copy the Public Key Token (highlighted, note that it would be different for you) and click Cancel button. Now go back to your ASPX page in the project and replace the incorrect Public Key Token with the one you just copied.

Redeploy your application. Start Internet Explorer and navigate to http://%5Bserver name]:[port]/_layouts/HelloSharePointWorld/Hello.aspx

You should see something similar to:

Congratulations, you have created your first SharePoint solution. If you start the Central Admin for SharePoint and go to Operations -> Solution Management you should see a solution named sharepointhelloworld.wsp.

Let’s add one more modification. Open the Hello.aspx.cs file and change it to:

using System;using Microsoft.SharePoint.WebControls;using System.Web.UI.WebControls;

public partial class Hello : LayoutsPageBase{ protected Label lblLabel1;

 protected void Page_Load(object sender, EventArgs e) {     lblLabel1.Text = "Hello world! Time is: " +     DateTime.Now.ToString(); }}

By that, we utilize the code behind to output data on the screen. Rebuild, redeploy and refresh the page.

This is a good starting point for further learning by experimenting and example.

Of course, the full code for this project can be found here.

How to create a K2 SmartObject

How does K2[blackpearl] integrate with other systems? One standard way to do this is through Web Services. And how does K2[blackpearl] integrate with Web Services? It is done through SmartObjects. So we have: K2 SmartObject Web Service Other System

“A K2 SmartObject can encapsulate data that does not currently reside in an existing system, such as data about an employee’s extracurricular activities, his or her family, or hobbies and interests. By using a SmartObject for employee information, all data about an employee is accessed in the same place, and updates are applied to the data at its source. It is important to remember that SmartObjects do not store or cache data. Once created, a SmartObject can be used and reused in a variety of ways; within K2 Designer for Visio, integrated for use in an InfoPath form, a workflow deployed via K2 Studio, in a report, in SharePoint, etc.”

SmartObject is all that and also it is the link between K2 and Web Services. So let’s see step by step how do we consume a Web Service method.

1. Create Web Service
First, let’s create a simple web service which we will be consuming later. For the purposes of the demonstration, it will be as simple as returning you the sum of two values.

I will use Visual Studio 2008 to create a new ‘ASP.NET Web Service Application’ project.

Let’s create the simple method ‘Sum’. See screenshot below:

Of course, you should test it first:

For the curious, it returned 3 😉

Ok, now we need to publish it somewhere in the network and make sure we can access it! Right click the solution, Publish.

‘Publish’ it somewhere on your local hard drive and make sure you map it in IIS to a virtual directory. You can do that by going into IIS, choose existing site (or create a new one), right click on it -> New Virtual Directory, browse to the path where you Published the service and keep clicking Next.

Let’s assume you published your service to http://localhost:86/SampleService/SampleService.asmx, which in the local network (if you have one) maps to

We are done with the service, it’s in the network, it works, it is ready to be registered as a service instance.

2. From K2 Workspace add it to Dynamic Web Service
Ok, now we have to add it to the ‘Dynamic Web Services’ list. Open your K2 Workspace. Go to Management -> Management Console -> Expand the servername -> SmartObjects -> Services -> Dynamic Web Service.

Click on the Add button and populate the URL field.

Click Next. In the next screen set names to something that makes sense to you and click Save. Let’s call it ‘Sample Service’.

You have now registered a service instance. Let’s verify that by going to the K2 server and opening C:\Program Files\K2 blackpearl\ServiceBroker (or C:\Program Files (x86)\K2 blackpearl\ServiceBroker) and run BrokerManagement.exe. On the first screen, click on ‘Configure Services’.
Under services -> DynamicWebService -> serviceinstances there should be an instance with a name ‘SampleService‘.

3. Create SmartObject
We are good to create a SmartObject around it now. Start a new instance of Visual Studio and create a new K2 SmartObject project.

The first thing you do is switch to Advanced mode.

Then Remove All methods that were created for you. We will start from scratch.

Then click on +Add and run the wizard that shows up in Advanced mode (it’s a checkbox on the first page).

Name the method Sum, make it Read type.

In the next screen, Configure Method Parameters, we will add the two parameters that we have to pass to the method – intA and intB. Make them ‘Number’s.

At the end, it should look like this:

Click Next. On the next screen you have to select the Service Method we will be executing. This is easy. First click Add, to start adding a service method call to the SmartObject. A new popup will show. There is a field ‘Service Object Method’ and next to it there is a ‘…’ button. Click on the button. It will open the Context browser. Browse through ServiceObjects Server -> Sample Service -> Sum -> Read.

Click Add in the Context Browser. You will see:

We need to map some fields now. See the ‘a’ and ‘b’ input properties? They are the web service method input parameters. We have to ‘bind’ them to the intA and intB fields we created for the SmartObject.

Click on the first one (‘a’), click ‘Assign’ button and a small pop up will appear. Set ‘Map to’ to ‘SmartObject Method Parameter’ and from the second dropdown select ‘intA’.

Then click ok and do the same thing for ‘b’, but select ‘intB’ instead. For ‘ReturnValue’ – click on it, Assign, set ‘Map To’ to SmartObject Property (i.e. you are storing the result in a property of the SmartObject). Then click Create.

You should end up with:

Click OK and finish the wizard.

Make sure you name your SmartObject properly.

4. Deploy
Right click the solution and do Rebuild. It should compile with no errors. Let’s deploy the SmartObject to our K2 Server. Right click the project -> Deploy. Follow the wizard (Next, Finish) and your SmartObject should be deployed.

5. Test
Let’s test your SmartObject. Log into the K2 server and go back to C:\Program Files (x86)\K2 blackpearl\ServiceBroker. Run the file SmartObject Service Tester.exe.

Expand the SmartObjects section, find your SampleServiceSmartObject, expand it until you see the Sum method.

Right click on it, Execute method. Populate the input fields and click ‘Sum’ button. Promptly, you should see the result displayed.

We just invoked the SmartObject, which talked to the WebService, retrieved result for us and was visualized in the test utility. Everything works perfectly, your SmartObject is ready to be consumed from a workflow!

6. Consume in Workflow
Create a new K2 Process Project in Visual Studio. I will reuse the K2 solution we already have and just add the new project to it.

From the Toolbox, drag and drop a new SmartObject event. On the second page, Wizard will prompt you for SmartObject Method. Browse to our Sum method in the SampleServiceSmartObject.

Click Add. It should look like this:

On the next screen, you have to provide values for the SmartObject input properties. This could be K2 DataFields from your process, or actual hardcoded sample values. For the purpose of the demo, we will just bind them to ‘2’ and ’54’. Select the property, click Assign button, set value, click ok. It should look like this:

On the next screen, we have to bind the return result to something. Let’s bind it to a DataField in the process which we will create. Click on the ReturnValue, click ‘Assign’, in the popup click on the ‘…’ button which will open the Context Browser. In this browser, either select already existing DataField, or create a new one on the fly (right click -> create). Make sure the field is Integer (for this demo). Select it, click ‘Add’, then ‘Ok’ in the pop up.

It should look like:

Click finish. You just finished adding a SmartObject event to your workflow. After this event is executed, you will have the value of ‘a’ + ‘b’ in the SumValue data field. I.e. you are consuming the SmartObject (and hence the Web Service).

Now you just need to finish the rest of the workflow 😉

7. Change the URL of the service
What if the address of your Web Service changes? Simple, go to the K2 server and open the C:\Program Files (x86)\K2 blackpearl\ServiceBroker folder. Run the BrokerManagement.exe app. On the first screen, click on ‘Configure Services’.

Find your service instance again:

Right click on it -> Configure Service. It will open a pop up. There you can change the URL field and click Save.

And that’s it, your service instance is now pointing to the right URL.

Basically that’s all. Now you know how to consume web services from your K2 Workflows, utilizing SmartObjects!

Hope this helps!

SharePoint bug tracking

So you have this SharePoint project you are working on… And as with any project, you need a bug tracker in order to communicate properly with the QAs and BAs in the company. What do you use? OnTime? Mantis? Bugzilla?

Why not SharePoint itself? It’s already (presumably) set up for you, users are familiar with it and you are only couple of steps away from setting up your own Issue Tracker.

To get it up and running, here is what you should do, step by step:
1. Open your SharePoint web site as admin

2. Click on ‘View All Site Content’ (usually top left) or open http://server:port/_layouts/viewlsts.aspx

3. Click on ‘Create’ to create a new list (shortcut: http://server:port/_layouts/create.aspx)

4. From the ‘Tracking’ column select ‘Issue Tracking’

5. Type in the name and description of the list where issues will be tracked

You may want to select ‘Yes’ on ‘Send e-mail when ownership is assigned’, to send emails to the users assigned to issues.

6. Click ‘Create’ button and your bug tracker is created. You should see a list with the name you have given on step 5. This is the tracker, open it. Click ‘New Item’ to see the default fields and their values you get.

7. Time to customize according to your needs. Go to the List Settings of your issue tracking list.

8. Scroll down to the Columns section. You will want to modify the highlighted columns – Issue Status, Priority and Category, for starter.

9. Click on any of them, let’s say ‘Issue Status’. Scroll down to the ‘Additional Column Settings’

You can now change the possible statuses your issues may have. For example, you may want to add ‘Acknowledged’, ‘Postponed’, ‘Invalid’, ‘Cannot fix’, etc… You may also change the default value or make the field required (a smart choice). When you are happy with your changes, click Ok.

10. Do the same for Category and Priority.

11. You can extend your tracker further, by adding extra columns. Let’s say, Product (as you may be working on different products at the same time) or Version (as your product may have many production versions). To do this, go to the Settings of the list again (as in step 7). Scroll down to columns and click Create Column.

12. Type in the name of your column and select its type. In this case, I will name it ‘Product Version’ and make it a dropdown list.

13. Make the column required (if you want to), specify the available choices and select the type of the control (dropdown, radio buttons or check boxes).

Then click Ok and you are all set.

14. Let’s test your bug tracker. Go to the list and create a new item

15. Fill all the fields you want to and click Ok. You have now logged a new Issue.

16. To customize your tracker further, you may want to create new Views, which sort and arrange the items in categories, assignees and what not

17. You may need to add users (give permissions) to the site or the list.

Now you have your own tracker, embedded in your SharePoint environment. Hope this was helpful!

The right way to SPWeb.EnsureUser in SharePoint

At some point of time you may need to call (SPWeb).EnsureUser from your custom SharePoint web application. But this method can not be called by everyone, as it requires some high level permissions. You may also get an error similar to this one:

Your solution is to wrap the EnsureUser within RunWithElevatedPrivileges call. However, there is a big catch. If you use instances of SPSite or SPWeb, obtained prior to the RunWithElevatedPrivileges block, it won’t work as expected because they are already associated to a non-elevated security context.

To illustrate it with code, here is WRONG usage of RunWithElevatedPrivileges:
SPWeb web = [… somehow obtained here…];

// NOTE: Wrong, do not use
SPUser someUser = web.EnsureUser(web.CurrentUser.LoginName);

And here is a CORRECT one:
SPWeb web = [… somehow obtained here…];

using (SPSite elevatedSite = new SPSite(web.Site.ID))
SPWeb elevatedWeb = elevatedSite.OpenWeb(web.ID);
SPUser someUser = elevatedWeb.EnsureUser(web.CurrentUser.LoginName);

Basically we used the IDs of the Web and Site objects, obtained prior to the elevated block, and used them to create Site and Web object within the elevated context.