Key factors for successfully implementing and launching an intranet

A couple of weeks ago, me and a customer of mine gave a lecture about social intranets and key factors for a successfull implementation.
Since we had been working together with an implementation of a social intranet for almost a year, we had some experiences that we wanted to share.

There have been a lot of articles published in the last decade, showing that the majority of  IT projects tend to fail in one perspective or the other. The main reasons for failure seams to be:

  • The budget wasn’t held
  • Not delivered within promised time frame
  • The product owner didn’t get what he/she wanted

There are some things that I believe will reduce the risk of failure. I’ll try to share the most important ones in this post.

Prestudy
Make sure that you make a thorough prestudy that answers the questions “What do I, as an employee, need (not what I would like) to do my daily work. This could result in a prioritised list that could be used as a starting point for the product backlog. Choose the top 10 from this list for the implementation project. Save the rest for after the project is done.

Implementation

  • Use an agile method, preferrably scrum, when implementing this.
  • Make sure that the requirements answers the questions [Someone] would like to do [Something] because [Business need]. This will make sure that the developers understands the specific requirement and therefore can decide on the best solution for implementing it.
  • Try to work with small prototypes and reqular meetings with the product owner. This will make the gap between business and IT smaller and decrease the risk of failure.
  • Make sure that you plan for how this application should be maintained after the project is done. If possible, make one of the developers from this phase join the project from the start.
  • Make sure that the product owner understands SharePoint’s potential and that a lot of requirements can be met at 80% with very little effort.

Launch
It’s important that you make a thorough launch plan for the intranet. Try to create a curioisity in the organisation before the launch so that the employees knows that something is up. In this case, the customer took inspiration from Google+ and Facebook launches by creating a reference group that had access early on. There were also serveral ambassadeurs that could spread the word.

On the morning of the launch, members of the project handed out breakfast bags to everyone with a little note that told the employee to sit down, eat the breakfast and take a look at the new intranet. This became a success and about 25% of all the employees wrote a micro blog post on the intranet at this day.

There are a lot of other factors to think about other than those I’ve mentioned. Depending on the organization you will be faced with challenages on many levels, both organizational and technical. The risk of failure will be reduced if the mentioned steps are followed.

What do you think?
Are there any other important factors that I haven’t mentioned?

How to get the Activity feed to show items older than 14 days

In a social intranet based on SharePoint 2010, a lot of functionality probably evolves around some kind of activity feed. The default Activit feed in SharePoint 2010 only displays activities newer than 14 days. In some cases there might be need for displaying older activities than that.

So, how do you make this work?
First of all, SharePoint stores the the value that regulates the timespan for the activities in a class called Limits (Microsoft.Office.Server.ActivityFeed) and the only way to set it is via reflection.
So we start by creating a new webpart for our activity feed. We do this by deriving from the abstrac class NewsFeedWebPartBase (Microsoft.SharePoint.Portal.WebControls).

In the example below, the limit is set to three weeks instead on two. Remember that this limitation is here for a reason, so think of the performance implications before changing it.

public class NewsFeed : NewsFeedWebPartBase
    {
        protected override void OnPreRender(object sender, EventArgs e)
        {
            DateTime minEventTime = DateTime.Now.AddDays(-21);
            var type = typeof(Limits);
            type.GetField("MAX_WindowIntoPast", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, DateTime.Now - minEventTime);
            base.ShowUserPicture = true;
            base.ActivityManager.MinEventTime = minEventTime;
            this.m_EventsCollection = base.ActivityManager.GetActivitiesForMe(this.MaxItemsToDisplay);
            base.OnPreRender(e);
        }
    }

Next, since the webpart derives from the NewsFeedWebPartBase, you need to use a .dwp file instead of a .webpart file for your webpart.
Here’s what it can look like:

<?xml version="1.0" encoding="utf-8"?>
<WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">
  <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
  <TypeName>MyNamespace.MyCustomNewsFeedWebPart</TypeName>
  <Title>$Resources:spscore,PeopleWebParts_ColleagueTracker_Title_Text;</Title>
  <Description>$Resources:spscore,PeopleWebParts_ColleagueTracker_Desc_Text;</Description>
  <PartOrder>3</PartOrder>
  <FrameType>TitleBarOnly</FrameType>
  <AllowMinimize>true</AllowMinimize>
  <AllowRemove>true</AllowRemove>
  <IsVisible>true</IsVisible>
</WebPart>

Now, go ahead and deploy the solution and activate the webpart feature.
Now navigate to the default.aspx for the MySite host, edit the page and replace the old “What’s new” Web part with your custom one.

Activities for the last three weeks are now displayed.

Some SharePoint events are not appearing in the activity feed

I recently stumbled on a strange problem. I’m currently working on a project with a lot of social features.
Central in the solution is the activity feed. Here we use both the standard activities, such as profile updates, tags for interests, birthdays and so on, and custom activities for things like if someone comments on your discussion board item.

We recently noticed that some of the standard activities, like tagging, profile updates and birthdays, didn’t show up in the activity feed.
I started a deep dive in the SharePoint source code and the stored procedures that are used and noticed something rather strange.

The timer job that creates the SharePoint standard activities (SharePoint Activity Feed Job, Microsoft.Office.Server.ActivityFeed.ActivityFeedGatherer) does so in the following manner:

  1. Get the last update time (Here’s where the the problem occurs)
  2. Create the profile property changes since (1)
  3. Create the comment activities for comments created after (1)
  4. Create the rating activities  for ratings made after (1)
  5. Create the tag activities  for tags created after (1)

The thing here is that the last update time isn’t when the timer job last ran. Instead, it calls the stored procedures dbo.activityFeed_GetLatestLastUpdateTimeConsolidated and dbo.activityFeed_GetLatestLastUpdateTimePublished. The procedures then looks into the dbo.ActivityEventsConsolidated and dbo.ActivityEventsPublished tables and picks the date for the most recent activity event. It then compares the two results and picks the oldest.

Here’s an example of the impact:

  1. At 12:00, I tag an article with the keyword Monkey.
  2. At 12:02, Someone responds to my discussion post, causing my custom code to create both consolidated and published events for this.
  3. At 12:04, The ActivityFeed timer job runs. It retrieves the last update time, which is 12:02. It looks for new tags, but there are no new tags after 12:02, so it doesn’t create an activity event for the tag.

I can think of some workarounds for this, but none of them I consider bulletproof:

  1. Make the Sharepoint ActivityFeed Timer Job run really often (Like every minute). This will minimize the the risk. Allthough, there is still a possibility that someone tags some content and then someone triggers a custom activity event to be created in the same minute.
  2. Instead of creating custom activity events directly, add them to a SharePoint list. Then create a timer job that will run right after the ActivityFeed Timer Job that gathers the list items and creates activity events of them.
    This solution might be more robust, but it takes a little longer to develop, and there still is a small risk that some standard activity will be created after the ActivityFeed Timer Job and before the custom timer job.

Has anyone expericenced the same thing? Any other idea of how to work around this problem?

Add a noteboard control to a custom page layout

I’ve been working with a social intranet for some time now, so I will try to share some of my most valuable findings on this subject.

First up is the SocialCommentControl, which is a good way for users to interact with eachother and the author of a specific page or article.
Unfortunateliy, by default this webpart is hidden in the top of all pages. I think that if you want users to be able to comment specific content, you should make it as easy as possible for them.

First of all, there’s a bug with the SocialCommentControl so that it gives away invalid HTML.
We will create our own control and fix this. We will then add the control to our custom page layout. Last but not least, I will show you how you can use the callback event of the control to take action if someone posts, edits or deletes a comment.

public class CustomSocialComments : SocialCommentControl, ICallbackEventHandler
{
    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        Controls.Add(new LiteralControl("</div>"));
    }
}

Now, we will add this control to our custom page layout.

Firs, we register the control on the page.

<%@ Register TagPrefix="SocialControls" 
       Namespace="Myproject.Social.Controls"
       Assembly="$SharePoint.Project.AssemblyFullName$" %>

Then we add the control to the page.

<SocialControls:CustomSocialComments id="CustomSocialComment1" runat="server" />

Now, while we’re at it, I’ll also show you how you can take action each time a user posts, edits or deletes a comment. For example, you might want to send some kind of notification to the author of the page, or the the users that has already posted a comment.

In the CustomSocialComments class, add the following code.

public void RaiseCallbackEvent(string eventArgument)
{
    var document = new XmlDocument();
    document.LoadXml(eventArgument);
    var documentElement = document.DocumentElement;
    if (documentElement != null)
    {
        var e = documentElement.GetAttribute("type");
        if (e != "Get")
        {
            if (e == "Delete")
            {
                //Take action if a comment is removed
            }
            else if (e == "Add")
            {
                var contentElement = (XmlElement)documentElement.SelectSingleNode("./RTEContents");
                var content = SPHttpUtility.NoEncode(contentElement.InnerText);
                //Take action if a comment is added
            }
            else if (e == "Edit")
            {
                var contentElement = (XmlElement)documentElement.SelectSingleNode("./RTEContents");
                var content = SPHttpUtility.NoEncode(contentElement.InnerText);
                //Take action if a comment is edited
            }
        }
    }
}

How to create an open dialog webpart

In this post I will describe how to create a webpart that creates a simple modal dialog. The webpart creates hyperlink that when clicked, opens a given URL in a SharePoint 2010 dialog.
It’s pretty hands on by using the SharePoint 2010 ECMA script function SP.UI.ModalDialog.showModalDialog.

First, create the properties that you want to expose. In this case, i created a property for the link text, link url, dialog title, dialog witdh and dialog height.

Then all you need to do is to add the script and the hyperlink to the page.

protected override void CreateChildControls()
 {
       StringBuilder sb = new StringBuilder();
       sb.Append("<script type=\"text/javascript\">");
       sb.Append("function openDialog() {");
       sb.Append("var options = {");
       sb.Append("url: \"" + LinkUrl + "\",");
       sb.Append("width: " + WindowWidth + ",");
       sb.Append("height: " + WindowHeight + ",");
       sb.Append("title: \"" + DialogTitle + "\",");
       sb.Append("};");
       sb.Append("SP.UI.ModalDialog.showModalDialog(options);");
       sb.Append("}");
       sb.Append("</script>");

       HyperLink a = new HyperLink();
       a.Text = this.LinkText;
       a.NavigateUrl = "#";
       a.Attributes.Add("onclick", "javascript:openDialog(); return false;");

       Controls.Add(new LiteralControl(sb.ToString()));
       Controls.Add(a);
}

No choices available for people fields in DataSheet View

I stumbled on a strange problem today.
A client asked why he couldn’t update the items in a list by using the datasheet view. The problem whas that there were no choices available for the user field. When he tried to write something himself, SharePoint gave him an error.

I looked at it and it turned out that the person field was missing the property List=UserInfo.

I made a small windows application that updated the field for him, using the SchemaXml to push in the property.

So the field should actually look something like this:

<Field ID="{00000000-0000-0000-0000-000000000000}"
       Type="User"
       DisplayName="My field"
       Required="FALSE"
       Group="My group"
       StaticName="MyField"
       Name="MyField" 
       List="UserInfo" />

Conclusion
To be able to modify person fields in datasheet view, the field must be installed with List=”UserInfo”.

Modified and Created not sortable in list view

Today, I received a query from a client who tried to sort a list based on the field Modified and Created.
The thing was that she couldn’t choose these fields in the sort settings of the view.

Apparently, in some cases when a list gets removed and then restored, these fields get the property CanToggleHidden set to false.

To solve this issue, I made a simple windows application that set the property back to true.

using (SPSite site = new SPSite("http://clientsite"))
{
    using (SPWeb web = site.OpenWeb())
    {
        string library = "PublishingPageImages"
        SPField field = web.Lists[library].Fields.GetFieldByInternalName("Modified");
        field.SchemaXml = "<Field ID=\"{28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f}\" ColName=\"tp_Modified\" RowOrdinal=\"0\" ReadOnly=\"TRUE\" Type=\"DateTime\" Name=\"Modified\" DisplayName=\"Ändrad\" StorageTZ=\"TRUE\" SourceID=\"http://schemas.microsoft.com/sharepoint/v3\" StaticName=\"Modified\" FromBaseType=\"TRUE\" Hidden=\"FALSE\" CanToggleHidden=\"TRUE\" />";
        field = web.Lists[library].Fields.GetFieldByInternalName("Created");
        field.SchemaXml = "<Field ID=\"{8c06beca-0777-48f7-91c7-6da68bc07b69}\" ColName=\"tp_Created\" RowOrdinal=\"0\" ReadOnly=\"TRUE\" Type=\"DateTime\" Name=\"Created\" DisplayName=\"Skapad\" StorageTZ=\"TRUE\" SourceID=\"http://schemas.microsoft.com/sharepoint/v3\" StaticName=\"Created\" FromBaseType=\"TRUE\" Hidden=\"FALSE\"/>";
        MessageBox.Show("Update successfull");
    }
}

Note that this site was using a swedish language pack, so the DisplayName property is set to the swedish display name of the fields.

Calculated field based on ID

A client asked me today why he can’t get his calculated field to work.
The field was a simple calculated field with the formula =ID. With other words, he wanted to copy the ID field to the calculated field.

The problem he was facing was that the field value was always 0 when the item was created. If the then went in and modified the calculated field, the value was updated.
I made some digging and found this article.

You cannot reference the ID of a row for a newly inserted row. The ID does not yet exist when the calculation is performed.

Solution/workaround:
I suggested that he made a small workflow in SharePoint Designer 2010 that did the copying for him, and then run this workflow automatically when an item is created.

Programmatically add Content By Query Web Part to a page

Sometimes it can be useful to programmatically be able to add a web part to a page. For example, in a SharePoint site, you might sometimes want to add a webpart to the default.aspx on the event WebProvisioned.
The code below shows how this can be done. It also shows how to configure the Content By Query Web Part.
In this example, it sets the web part to display all the items of content type article page, located on the current site and all subsites

using (SPLimitedWebPartManager spm = SPContext.Current.Web.GetLimitedWebPartManager(
"Pages/default.aspx", System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared))
{
    ContentByQueryWebPart cqwp = new ContentByQueryWebPart();
    cqwp.ContentTypeBeginsWithId =
    "0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D";
    cqwp.WebUrl = SPContext.Current.Web.ServerRelativeUrl;
    cqwp.Title = "News";
    spm.AddWebPart(cqwp, "MiddleLeftZone", 1);
}