Oct
6
2008

How to use SharePoint Delegate Control to change the navigate URL of Manage Links (My Links)

Microsoft Office Online -

By using the My Links menu, you can easily add new links, reorganize your links, access sites where you are a member, and click links to save them to you My Links list.

You can use this feature when you have "My Site" setup and running in your SharePoint Shared Services.

James Tsai .NET SharePoint Blog - My Links Manage Links menu control

Problem

Let's say you have following two site collections setup for your SharePoint intranet site and My Site.

Intranet - http://intranet/

My Site - http://mysite/

When user is accessing manage links page by clicking on My Links, Manage Links menu on Intranet site, the menu control redirects user to

http://mysite/_layouts/MyQuickLinks.aspx

And you probably don't want this to happen, because of following reasons

  • You want to hide My Site from user, but you still want to use My Links feature.
  • You don't want user to navigate away from current site collection when they clicked on the Manage Links menu.
  • You want MyQuickLinks.aspx page to inherit and use current site theme.
  • You are experiencing the MOSS SP1 bug I described in last post.
  • OR you just want to customize it to navigate to the any URL

Goal

The goal here is obvious. To customize the navigate URL of Manage Links. In the example above, user should be redirected to

http://intranet/_layout/MyQuickLinks.aspx

Solution

You'll need following three things to achieve the goal

More...

Oct
4
2008

Unknown Error - Manage Links of My Links

Do you get "Unknown Error" page when you try to access "Manage Links" page from "My Links"? Don't panic.

Many people experienced the same problem after Microsoft Office SharePoint Server SP1 update. The problem was confirmed by Microsoft and hotfix for it has been around for awhile (KB 952294).

Microsoft Help and Support:

You do not have the Create Personal Site permissions in SharePoint Server 2007 Service Pack 1. When you click the Manage Links link, you may receive the following error message:

An unknown error has occurred.

If you are looking for a quick fix, I recommend you go view and request hotfix package from Microsoft. But if you really want to customize Manage Links redirecting URL, in next post I'll show you how to do it without making any changes to master page.

Watch this space

Sep
26
2008

SharePoint Data View / Data Form Web Part - Group items by month on DateTime field

This is one of the most popular requirements I always get from clients. Especially when they have large collection of documents and want to give their user an easy way to browse document items in Data Form Web Part (DFWP).

For example, you have a list of documents displayed in DFWP and each document has "Published Date" field. How can you group DFWP items by its "Published Date" month value?

Goal

James Tsai .Net SharePoint Blog - DFWP Group By Month Final

Solution

Luckily, all you need to do is to change a few lines in XSL that renders your DFWP. Here is step by step of how to do it.

1. Add "Group By" to Data Form Web Part.

Here is what your original DFWP should look like without any "Group by" on field

James Tsai .Net SharePoint Blog - DFWP default view without Group by field

In SharePoint Designer (SPD), open DFWP's Common Data View Tasks and select "Sort and Group"

James Tsai .Net SharePoint Blog - DFWP common data view tasks

Select DateTime field you want to Group by, In this example it is "Published Date". "Show group header" is also selected here because this way you can see what values are used to group items

James Tsai .Net SharePoint Blog - DFWP sort and group options

After above steps, you can see the DFWP is correctly grouped on "Published Date" field. But it treats each DateTime value as a different group value.

By default, DFWP group DateTime field based on their actual Date (YYYY-MM-DD) value. Not just year and month (YYYY-MM).

James Tsai .Net SharePoint Blog - DFWP Group By DateTime Field Default

Since requirement here is to group items with same month (item with "Publsihed Date" 08/16/2008 and 08/03/2008 in this example) in same group. Further steps are needed.

2. Modify XSL

Inside the XSL that renders your DFWP, search for "dvt_groupfield". If you use "Search All" within XSL, <xsl:when test="not ($dvt_groupfield)"> is the line you want.

James Tsai .Net SharePoint Blog - DFWP XSL Search For DVT_GroupField

And you will see this section,


<xsl:when test="not ($dvt_groupfield)">
        <xsl:value-of select="ddwrt:NameChanged(string(@PublishedDate), 0)" />
</xsl:when>

Change this to,


<xsl:when test="not ($dvt_groupfield)">
        <xsl:value-of select="ddwrt:NameChanged(string(substring(@PublishedDate,1,7)), 0)" />
</xsl:when>

Above change is critical, that's where you specify how you want to group items in DFWP.

The original @PublishedDate value is presented in format "YYYY-MM-DDTHH:MM:SSZ". substring(@PusblishedDate,1,7) gives us "YYYY-MM" which is what we want DFWP to group by. Note: In XSL, index starts from 1 not 0.

You will see this after above changes

James Tsai .Net SharePoint Blog - DFWP XSL After Change Group By Value

Items are now group correctly, but group header still displaying incorrect text. Because In DFWP XSL, group value and header value are generated from different template. You have changed first one (in above step), and now last step is to change header value.

Just scroll down from where you changed group value in XSL a bit, and you should see this line


<xsl:when test="not (@PublishedDate) and (@PublishedDate) != false()"><xsl:value-of select="' '" /></xsl:when>
        <xsl:otherwise>
                <xsl:value-of select="ddwrt:GenDisplayName(string(@PublishedDate))" />

Change ddwrt:GenDisplayName(string(@PublishedDate)) to substring(@PublishedDate,1,7). Like following,


<xsl:when test="not (@PublishedDate) and (@PublishedDate) != false()"><xsl:value-of select="' '" /></xsl:when>
        <xsl:otherwise>
                <xsl:value-of select="substring(@PublishedDate,1,7)" />

And you should get this as result

James Tsai .Net SharePoint Blog - DFWP XSL After Change Heading Values

You can also change Group Heading to display in format YYYY-MMM (like the one in first screen shot) , or anything you like by changing above XSL.

Sep
17
2008

Why "Start this workflow to approve publishing a major version of an item" option is disabled for my custom workflow in SharePoint?

James Tsai .Net SharPoint Blog - Start Workflow To Approve Major Version Item

This is a common question from developer who is creating his/her first custom workflow for SharePoint.

Turn on "Create major and minor (draft) versions" on document library is definitely a right way to go :) But the problem usually fall in one of following two scenarios

Scenario 1 - Missing value in InitiationType Element

Make sure in your workflow definition (workflow.xml) you have included OnMajorCheckIn in <InitiationType>. For example


<MetaData>

    <InitiationType>Manual;#OnNewItem;#OnItemUpdate;#OnMajorCheckIn

    </InitiationType>

</MetaData>

MSDN - "If you do not specify an InitiationType element, Windows SharePoint Services treats the workflow as if the Manual, OnNewItem, and OnItemUpdate values have been specified"

Scenario 2 - The option is enable to you, but you can't select it.

Since "major and minor versions" only supported in Document Library. "Start this workflow to approve publishing a major version of an item" option was never meant to work with List. So make sure you only associate your custom workflow to Document Library if you want it to work. If you do it via code, make sure this is how you do it:

SPList _list = _spweb.Lists["Documents"];
SPDocumentLibrary docslib = (SPDocumentLibrary)(_list)
//Add workflow to document lib not list
docslib.AddWorkflowAssociation(yourWorkflow);

 

Hope it helps

Sep
13
2008

Understand SharePoint Permissions - Part 2. Check SharePoint user/group permissions with Permissions web service and JavaScript

Goal

In Part 1. I have mentioned that you can check user permissions without using any of SharePoint OM, or you can perform permission check on remote site/application. In this post you are going see a simple example of how to do it.

The goal here is to create a new web application with web service reference to SharePoint Permission web service. This web application only has one default.aspx page with two drop down lists on it.

First drop down list (DDL) pre-populates data with SharePoint users/groups name returned by GetPermissionCollection method of Permissions web service. Second DDL contains list of SPBasePermissoions permissions.

JavaScript function will be used to check if user/group selected in first DDL has permission right selected in second DDL. An image will be displayed to indicates the result.

James Tsai .Net SharePoint Blog - Two Drop Down Lists

Project Setup

Create a new ASP.NET project with standard project template and then add Web Service reference. Two image files also included in project for displaying the result.

James Tsai .Net SharePoint Blog - New web application with WS reference

The web service URL is http://<site url>/_vti_bin/permissions.asmx and we named it SharePointPermissionsService

James Tsai .Net SharePoint blog - Add Permission Web Service Reference

Coding

Create Drop Down Lists



<!-- default.aspx -->

<div style="float:left;">

        <asp:DropDownList ID="ddlUserGroup" runat="server" />

        <select name="ddlPermissionSet" id="ddlPermissionSet" onChange="DoPermissionCheck()">

                <option value="0x0000000000000001">ViewListItems</option>

                <option value="0x0000000000000002">AddListItems</option>

                <!--....................more permissions here........-->

                <option value="0x0000010000000000">EditMyUserInfo</option>

        </select>

<div>

<div id="divFailed" style="display:none">

                <img src="Image/failed.gif" />

</div>

<div id="divPassed" style="display:none">

                <img src="Image/passed.gif" />

</div>

First div contains two drop down lists. One is ASP DropDownList control and its options are loaded from server side. And other one is normal html drop down list with options from the permissions table from Part 1.

The first ASP DropDownList control is pre-populated with following code

/**default.aspx.cs**/

//Add reference to web serivce proxy we created earlier
using SharePointPermissionsService;
//Add other references here
public partial class _Default : System.Web.UI.Page
{   
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
        /*Create web service instance*/
            Permissions p = new Permissions();
        /*make call to GetPermissionCollection method with site name "Sandbox" and type "Web"*/
            XmlNode node = p.GetPermissionCollection("Sandbox", "Web");
            using (XmlNodeReader reader = new XmlNodeReader(node))
            {
        /*load dataset from xmlreader*/
                DataSet ds = new DataSet();               
                ds.ReadXml(reader);
        /*data binding*/
                ddlUserGroup.DataSource = ds.Tables[1];
                ddlUserGroup.DataTextField = "GroupName"; //bind display text to GroupName
                ddlUserGroup.DataValueField = "Mask";  // bind option value to Mask value
                ddlUserGroup.DataBind();
            }
        }
    }
}

Remember, you must make sure the user running the context has permission to access SharePoint web service. Or you can use

p.Credentials = <create new credential here> to call web service with specific credential in the code above.

GetPermissionCollection returns data in following format


    <Permissions>

        <Permission

            MemberID="3"

            Mask="138612801"

            MemberIsUser="False"

            MemberGlobal="True"

            GroupName="Viewers"

        />

        <Permission

            MemberID="4"

            Mask="1011028719"

            MemberIsUser="False"

            MemberGlobal="True"

            GroupName="Sandbox Members"

        />

        <!--.....More permissions here.....-->

        <Permission

            MemberID="13"

            Mask="134287360"

            MemberIsUser="False"

            MemberGlobal="True"

            GroupName="Quick Deploy Users"

        />

    </Permissions>

</GetPermissionCollection>

Two drop down lists will be displayed as following

James Tsai .Net SharePoint Blog - User Group Name Drop Down List

James Tsai .Net SharePoint Blog - Permission Drop Down List

Create permission check function

Permission DDL has its onClick event registered with DoPermissionCheck() JavaScript function. DoPermissionCheck() is where you perform bitwise operation to compare user/group mask with each permission.

DoPermissionCheck() does four things

1. Get selected values of both DDL

2. Convert selected decimal value from ddlUserGroup DDL to Hex base16.

3. Get high and low masks from user mask and permission mask (in Hex base 16)

4. Perform bitwise AND operation on two high masks and two low masks.

If you look closer, you will find it has similar implementation to HasRights(), EqualRights(), SetCurrentPermMaskFromString() functions from CORE.JS in SharePoint. SharePoint uses these three JavaScript functions to check user permissions (like what we doing here) to display correct list item context menu for user.

/*default.aspx*/

function DoPermissionCheck()
{
        /*get first dropdownlist selected value*/
        var uSelectedIndex = document.getElementById("<%Response.Write(ddlUserGroup.ClientID);%>").selectedIndex
        var uSelectedValue = document.getElementById("<%Response.Write(ddlUserGroup.ClientID);%>").options[uSelectedIndex].value
        /*get second dropdownlist selected value*/
        var pSelectedIndex  = document.getElementById("ddlPermissionSet").selectedIndex
        var pSelectedValue = document.getElementById("ddlPermissionSet").options[pSelectedIndex].value           
        /*convert user mask to hex base 16 (use toString(16))*/
        var maskInDecimal = parseInt(uSelectedValue);
        var userP = maskInDecimal.toString(16);       
        var requiredP = pSelectedValue;
        /*get masks length*/
        var requiredPL = requiredP.length;
        var userPL = userP.length;            
        /*get high and low permisison mask    */
        var requiredPermMaskH=parseInt(requiredP.substring(2, requiredPL - 8), 16);
        var requiredPermMaskL=parseInt(requiredP.substring(requiredPL - 8, requiredPL), 16);           
        /*get high and low user/group mask*/
        var userPermMaskH;
        var userPermMaskL;
        if(userP.length <=10 )
        {
            userPermMaskH=0;
                userPermMaskL=parseInt(userP,16);
        }
        else
        {
                userPermMaskH=parseInt(userP.substring(2, userPL - 8), 16);
                userPermMaskL=parseInt(userP.substring(userPL - 8, userPL), 16);
        }                                     
        /*do bitwise AND operation*/
        if(((requiredPermMaskL & userPermMaskL)==requiredPermMaskL)
                && ((requiredPermMaskH & userPermMaskH)==requiredPermMaskH))
        {
                document.getElementById("divPassed").style.display = "";
                document.getElementById("divFailed").style.display = "none";
        }
        else
        {
                document.getElementById("divPassed").style.display = "none";
                document.getElementById("divFailed").style.display = "";
        }
}

Now you can select different user/group and permission to see the image change.

James Tsai .Net SharePoint Blog - User Has No Permission example

And of course you can implement above method in C# to perform permission check on server side. You can also extend this control to check user permissions on list level. (If your list does not inherit permissions from parent web site)

Just change web service call to GetPermissionCollection(<list name>, "List");

Easy, isn't it?