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?

Sep
7
2008

Understand SharePoint Permissions - Part 1. SPBasePermissions in Hex, Decimal and Binary - The Basics

SPBasePermissions always reminds me of  "Introduction to Computer System" course from year one in university. It was Implemented based on the same basic and simple technique that almost used by every applications with permission system. But as high level programming language developer, we often forget how it works fundamentally.

For example, you know by calling SPWeb.DoesUserHavePermissions() you can check permissions granted by user. But what if this method has not been implemented or when you have no reference to Microsoft.SharePoint.dll? It is time to use "&","^" operators to work out the basics.

I am breaking this topic into two parts. Part 1. shows how is SPBasePermissions implemented. And in Part 2. you will see how to work out user permissions manually without calling DoesUserHavePermissions() or any other SharePoint code.

/*SPBasePermissions enum*/

public enum SPBasePermissions : ulong
{
    AddAndCustomizePages = 0x40000L,
    AddDelPrivateWebParts = 0x10000000L,
    AddListItems = 2L,
    ApplyStyleSheets = 0x100000L,
    ApplyThemeAndBorder = 0x80000L,
    ApproveItems = 0x10L,
    BrowseDirectories = 0x4000000L,
    BrowseUserInfo = 0x8000000L,
    CancelCheckout = 0x100L,

    /*...*/

}

As you can see SPBasePermissions enum is representing in hex and stored as unsigned long. The complete table of permissions in both hex and decimal are in following table.

Permission Name Hex (base 16) Decimal
EmptyMask 0x0000000000000000 0
List and Document permission    
ViewListItems 0x0000000000000001 1
AddListItems 0x0000000000000002 2
EditListItems 0x0000000000000004 4
DeleteListItems 0x0000000000000008 8
ApproveItems 0x0000000000000010 16
OpenItems 0x0000000000000020 32
ViewVersions 0x0000000000000040 64
DeleteVersions 0x0000000000000080 128
CancelCheckout 0x0000000000000100 256
ManagePersonalViews 0x0000000000000200 512
ManageLists 0x0000000000000800 2048
ViewFormPages 0x0000000000001000 4096
Web level permission    
Open 0x0000000000010000 65536
ViewPages 0x0000000000020000 131072
AddAndCustomizePages 0x0000000000040000 262144
ApplyThemeAndBorder 0x0000000000080000 524288
ApplyStyleSheets 0x0000000000100000 1048576
ViewUsageData 0x0000000000200000 2097152
CreateSSCSite 0x0000000000400000 4194314
ManageSubwebs 0x0000000000800000 8388608
CreateGroups 0x0000000001000000 16777216
ManagePermissions 0x0000000002000000 33554432
BrowseDirectories 0x0000000004000000 67108864
BrowseUserInfo 0x0000000008000000 134217728
AddDelPrivateWebParts 0x0000000010000000 268435456
UpdatePersonalWebParts 0x0000000020000000 536870912
ManageWeb 0x0000000040000000 1073741824
UseRemoteAPIs 0x0000002000000000 137438953472
ManageAlerts 0x0000004000000000 274877906944
CreateAlerts 0x0000008000000000 549755813888
EditMyUserInfo 0x0000010000000000 1099511627776
Special Permissions    
EnumeratePermissions 0x4000000000000000 4611686018427387904
FullMask 0x7FFFFFFFFFFFFFFF 9223372036854775807

 

From table above, It is obvious that each permission also represents a single binary digit. And bitwise OR can be used when you assigning multiple permissions to single role.

For example, users with ViewListItems, EditListItems, AddListItmes and DeleteListItems permissions will have decimal 15 or hex 0xF as their permissions mask.

       0001 (0x1, 1) ViewListItems
       0010 (0x2, 2) EditListItems
       0100 (0x3, 4) AddListItmes
  OR 1000 (0x4, 8) DeleteListItems
    = 1111 (0xF, 15)

This is basically how permission level works in SharePoint. - More code examples in part 2 .

Aug
20
2008

SPRegionalSettings.GlobalTimeZones - How to build world clock / get time zones information in SharePoint

Ok, you probably do not want to create world clock web part from scratch. Many free ones over Internet. Not to mention the very popular world clock web part from "bamboo Solutions".

But if you really want to create one by yourself for whatever the reason is, or you just want show the local time for employees working in different cities around the world (in their profile page, maybe). It can be done easily without to use any external web services, or hack your way through windows registry to get time zones list.

Yes, thanks to SharePoint OM. You can get all the information you want from Microsoft.SharePoint.dll

SPRegionalSettings.GlobalTimeZones

It returns you a collection of SPTimeZones objects used in Windows SharePoint Services.

SPTimeZoneCollection timeZoneColl = SPRegionalSettings.GlobalTimeZones;
foreach (SPTimeZone tz in timeZoneColl)
{
    DateTime currentLocalDateTime = DateTime.Now;
    DateTime currentDestDateTime = tz.UTCToLocalTime(currentLocalDateTime.ToUniversalTime());
        Console.WriteLine("ID: {0}, DateTime: {1}, Description: {2}", tz.ID, currentDestDateTime.ToString(), tz.Description);
}

In above code sample, it loop through each SPTimeZone inside SPTimeZoneCollection. Writes ID, current date time (in local server date time format) and description in console.

This is what you will get

James Tsai .Net SharePoint VSTO C# ASP.NET Blog - SPRegionalSettings GlobalTimeZones Code Sample

As you can see, my local time in Sydney, Australia is Thursday, August 21, 2008 4:01pm. And time in Tokyo, Japan is 08/21/2008 3:01pm. In Auckland, New Zealand is 08/21/2008 6:01pm. Which are correct!

//This line of code converts SPTimeZone object from UTC to your local time
DateTime currentDestDateTime = tz.UTCToLocalTime(currentLocalDateTime.ToUniversalTime());

Basically that's all you need to create world clock web part, or date time conversion control in SharePoint site. You can either have GlobalTimeZones collection as drop down list for user to select the time zone for displaying date time. Or you can create a SharePoint list mapping between cities name with time zone ID in GolbalTimeZones collection.

James Tsai

Blog Disclaimer