Wednesday, February 20, 2013

SharePoint 2010: Small workarounds for some big problems.

Sometimes we face challenges during development and customization for SharePoint applications and we find some really simple solutions to achieve required functionality. I also came across such situations and found some really small and simple workarounds to resolve the issues. This post is all about sharing my experience regarding that.


1. Read only Gantt view:
There are many people who are using Task list and want to display task details in Gantt view but they don't want to enable users to edit task items from Gantt view and permissions cannot be set at view level. They want their users to perform editing through edit forms because most of the times you don't include all the fields in Gantt view. I also came across such requirement. Although there are many solutions available on internet for this but I have found a simpler one and thought it to be worth sharing here.

In this solution all you have to do is to create calculated columns for all the fields required or you want to show in Gantt View. In my case those fields were Title, Start Date, End Date, so I created 3 calculated columns:
  • Title_C
          Formula: =[Title]
          The data type returned from this formula is:
  • Start Date C
           Formula: =[Start Date]
          The data type returned from this formula is:

  • End Date C
           Formula: =[End Date]
          The data type returned from this formula is:


  • :
     
  • Gantt Columns: 
     
    

Here is the output I have got as a read only Gantt View


For Gantt View Toolbar, I have placed following code in a Content Editor Webpart:

css style to hide edit and delete options of GanttView:
<style>
        .ms-MenuUIRtL
        {
            display: none !important;
        }
        .clip16x16
        {
            display: none !important;
        }
        .clip9x6
        {
            display: none !important;
        }
    </style>

   
HTML to create Gantt view toolbar:
<a class="ms-cui-ctl-medium " id="Ribbon.List.GanttView.ZoomIn-Medium" role="button"
        href="javascript:WebPartWPQ2_JSGridController.ZoomInGantt();" unselectable="on"
        mscui:controltype="Button"><span class="ms-cui-ctl-iconContainer" unselectable="on">
            <span class=" ms-cui-img-16by16 ms-cui-img-cont-float" unselectable="on">
                <img src="/_layouts/1025/images/formatmap16x16.png" unselectable="on" alt="" style="top: -18px;
                    left: -58px" />
            </span></span><span class="ms-cui-ctl-mediumlabel" unselectable="on">Zoom In</span>
    </a><a class="ms-cui-ctl-medium " id="Ribbon.List.GanttView.ZoomOut-Medium" role="button"
        href="javascript:WebPartWPQ2_JSGridController.ZoomOutGantt();" unselectable="on"
        mscui:controltype="Button"><span class="ms-cui-ctl-iconContainer" unselectable="on">
            <span class=" ms-cui-img-16by16 ms-cui-img-cont-float" unselectable="on">
                <img src="/_layouts/1025/images/formatmap16x16.png" unselectable="on" alt="" style="top: -242px;
                    left: -178px" />
            </span></span><span class="ms-cui-ctl-mediumlabel" unselectable="on">Zoom Out</span>
    </a><a class="ms-cui-ctl-medium " id="Ribbon.List.GanttView.ScrollToTask-Medium"
        role="button" href="javascript:WebPartWPQ2_JSGridController.ScrollGanttToTask();"
        unselectable="on" mscui:controltype="Button"><span class="ms-cui-ctl-iconContainer"
            unselectable="on"><span class=" ms-cui-img-16by16 ms-cui-img-cont-float" unselectable="on">
                <img src="/_layouts/1025/images/formatmap16x16.png" unselectable="on" alt="" style="top: -34px;
                    left: -242px" />
            </span></span><span class="ms-cui-ctl-mediumlabel" unselectable="on">Scroll to Task
                Item</span> </a>


Note: "WebPartWPQ2_JSGridController" is object for Gantt View web part. This might be different in your case. So you have to find out the correct object (from view source or any development toolbar of web browser) and change the HTML accordingly for this solution.

2. HTML in calculated/single line of text field:
Once I had to place HTML links in XsltListViewWebPart and I had to send some data from list item in query string to the target page like ID of list item. I created a calculated column with following formula:

=CONCATENATE("<DIV><a href='http://mysite/pages/TargetPage.aspx?ItemID=", [ItemID] , "' > link text </a></DIV>")

This did not format HTML links in list item data so I needed to place following javascript in a Content Editor WebPart on same page to format links for all items in webparts:

<script type="text/javascript">
        function FromatHTML() {
            var theTDs = document.getElementsByTagName("TD");
            var i = 0;
            var TDContent = " ";
            while (i < theTDs.length) {
                try {
                    TDContent = theTDs[i].innerText || theTDs[i].textContent;
                    if ((TDContent.indexOf("<DIV") == 0) && (TDContent.indexOf("</DIV>") >= 0)) {
                        theTDs[i].innerHTML = TDContent;
                    }
                }
                catch (err) { }
                i = i + 1;
            }
        }

        FromatHTML();
</script> 

This script will format the HTML in list item fields of XsltListViewWebPart.
 
3. Hide Add New Item link from XsltListViewWebPart:
Once I needed to add some XsltListViewWebParts on a single page and I was asked to remove "Add new item" option from each web part. So I had to hide "Add new item" link from the bottom of every webpart. For this I just added following css style on the page and it made the link invisible from the page for all web parts:

<style type="text/css">
TD.ms-addnew {
    DISPLAY: none !important
}</style>

4. Hide edit/delete button from display/edit form's ribbon using javascript:
Here is another problem with a simpler solution. Display and Edit form Ribbon has edit and delete option. But in my case I had to disable editing and deletion for a particular list item once it has passed through a stage of approval workflow. Before that approval stage only owner and approvers can edit and delete the list item. So I had some conditions where I need to show edit and delete options and for the rest of the cases I had to disable or hide these options. Well this did not seem to be achieved through any Out Of The Box (OOTB) feature or options and I was also not interested to write any event receiver for this scenario.
This was accomplished through a custom web part having a panel with following javascript and this panel was rendered/made visible programatically:

<script type="text/javascript">

        function HideDeleteOption() {
            try {                    document.getElementById("Ribbon.ContextualTabs.InfoPathListDisplayTab.Manage.Controls.btnDelete-Medium").style.display = "none";
            }
            catch (ex) {
            }
        }

        function HideEditDeleteOption() {
            try {                document.getElementById("Ribbon.ContextualTabs.InfoPathListTab.Actions").style.display = "none"
            }
            catch (ex) {
            }
        }

        function HideEditButton() {
        try {
            document.getElementById("Ribbon.ListForm.Display.Manage.EditItem-Large").style.display = "none";
        }
        catch (ex)
        { }
    }
        setTimeout("HideDeleteOption();", 100);
        setTimeout("HideEditDeleteOption();", 100);
        setTimeout("HideEditButton()", 100);

</script>  

This webpart was added on Display and Edit Forms for the list.     

Monday, February 11, 2013

SharePoint 2010: Set default values in list form fields using javascript.

SharePoint 2010 provides out of the box functionality to set default values in fields of any list form through list settings but there are some limitations regarding this option. These values are either static or simple formula based with logged-in user and today's date variation and we also cannot select default values for lookup fields. Sometimes you need to set default values to the form fields which are dynamic or on demand, like values from query string, based on any status field or select lookup field by value or text.

This post is all about a simple solution to set default values in list forms using some javascript. Javascript methods here parses page's HTML objects, locates required field and sets default values to the field.

Following are some javascript functions used for this purpose:

1. GetTagFromIdentifierAndTitle

function GetTagFromIdentifierAndTitle(tagName, identifier, title) {
         var idLength = identifier.length;
         var tags = document.getElementsByTagName(tagName);
         for (var i=0; i < tags.length; i++) {
              var tagID = tags[i].id;             
              if (tags[i].title == title && (identifier == "" || tagID.indexOf(identifier) == tagID.length - idLength)) {
                   return tags[i];
              }
          }
          return null;
    }


This is the key function for this solution. This functions locates HTML object  of a form field on the page. It takes three parameters:
  • tagName: Name of the form field's HTML tag.
  • identifier: type of the form field. (optional parameter, empty string "" can also be used)
  • title: Title of the form field, mostly it is the display name of the field
i) For single line of text, number, currency and yes/no:
   tagName: input
   identifier: BooleanField for yes/no field and TextField for others.

ii) For multiple line of text:
   tagName: textarea (for simple text not for rich text)
   identifier: TextField

iii) For lookup:
   tagName: select
   identifier: lookup

2. SetLookupFromFieldName and SetSelectedOption

function SetLookupFromFieldName(fieldName, value) {
         if (value == undefined) return;
         var theSelect = GetTagFromIdentifierAndTitle("select","Lookup",fieldName);

         if (theSelect == null) {
              var theInput = GetTagFromIdentifierAndTitle("input","",fieldName);             
              document.getElementById(theInput.optHid).value = value;             
         } 

        else {
              SetSelectedOption(theSelect, value);
         }
    }


    function SetSelectedOption(select, value) {
         var opts = select.options;
         var optLength = opts.length;
         if (select == null) return;

         for (var i=0; i < optLength; i++) {

              if (opts[i].value == value) {
                   select.selectedIndex = i;
                   return true;
             }
        }
         return false;
    }


These two functions can be used to set lookup fields based on value property. 
  • fieldName: Title or display name of the form field.
  • value: Value of the option needs to be selected as default.

3. SetLookupFromFieldNameByText and SetSelectedOptionByText:

function SetLookupFromFieldNameByText(fieldName, text) {
    try
    {
         if (text == undefined) return;
         var theSelect = GetTagFromIdentifierAndTitle("select","Lookup",fieldName);

         if (theSelect == null) {
              var theInput = GetTagFromIdentifierAndTitle("input","",fieldName);
              ShowDropdown(theInput.id);
              var opt=document.getElementById(theInput.opt);
              SetSelectedOptionByText(opt, text);
              OptLoseFocus(opt);
         } 

         else {
              SetSelectedOptionByText(theSelect, text);
         }
    }
    catch(ex)
    {
    }
    }
   
    function SetSelectedOptionByText(select, text) {

         var opts = select.options;
         var optLength = opts.length;
         if (select == null) return;

         for (var i=0; i < optLength; i++) {

              if (opts[i].text == text) {
                   select.selectedIndex = i;
                   return true;
             }
        }
         return false;
    }


These two functions can be used to set lookup fields based on text property. 
  • fieldName: Title or display name of the form field.
  • text: Text of the option needs to be selected as default.

4. GetQueryStringValue:

    function GetQueryStringValue(variable)
    {
        var queryString = location.search.substring(1, location.search.length);
      
        var args = queryString.split("&");
        var queryStringValues = new Object();     
      
        for (var i=0; i < args.length; i++) {
            var nameVal = args[i].split("=");
            var temp = unescape(nameVal[1]).split('+');
            nameVal[1] = temp.join(' ');
            queryStringValues[nameVal[0]] = nameVal[1];
        }

        return queryStringValues[variable];
    } 


This function returns value from query string variable.


Usage:

Set default value in Title field of list form:
GetTagFromIdentifierAndTitle("input","","Title").value = "test default value for title";
 or
GetTagFromIdentifierAndTitle("input","TextField","Title").value = "test default value for title";

Set default value for the lookup field with display name "Category":
var lookupId =  GetQueryStringValue("lookupId");
SetLookupFromFieldName("Category", lookupId );

 var lookupText =  GetQueryStringValue("lookupText");
SetLookupFromFieldNameByText("Category", lookupText);

You can place all these functions in a Content Editor Web Part (CEWP) along with a function which sets/selects default values in form fields.

Friday, February 8, 2013

SharePoint 2010 OOTB ratings, how to get individual ratings for any list item or by particular user

SharePoint 2010 provides out of the box feature to enable users to rate any list item. This can be achieved with few steps of configuration. Well this is actually not the context of this post to show you how to enable ratings on any list and all the stuff available through out of the box functionality. Let's talk about something which is not available out of the box and you might come across such requirement while using out of the box rating feature.
The out of the box rating control provides you information about average rating done by distinct users and number of distinct users who have rated a particular item. In this post we will discuss how to fetch individual ratings done for a particular list item and ratings done by particular user.

1. Individual rating details done for a list item:
Following code can be used to fetch individual ratings done for a list item (SPListItem listItem1) belongs to list (SPList list1):

SPSite site = SPContext.Current.Site;
SPWeb web = SPContext.Current.Web;
SPServiceContext context = SPServiceContext.GetContext(site);
SocialRatingManager socialRatingManager = new SocialRatingManager(context);
string listItemURL = web.Url + "/_layouts/listform.aspx?PageType=4&ListId={" + list1.ID.ToString() + "}&ID=" + listItem1.ID;

SocialRating[] ratings = socialRatingManager.GetRatings(new Uri(listItemURL));
foreach (SocialRating rating in ratings)
{
    if (rating.Rating > 0)
    {

         string userName = rating.Owner.DisplayName,
         int rating = rating.Rating;
    }


2. Ratings done by particular user:
Following code can be used to fetch ratings done by particular user (SPUser user1):

SPSite site = SPContext.Current.Site;
SPServiceContext context = SPServiceContext.GetContext(site);
SocialRatingManager socialRatingManager = new SocialRatingManager(context);
UserProfileManager profileManager = new UserProfileManager(context);
UserProfile profile = profileManager.GetUserProfile(user1.LoginName.ToString());

SocialRating[] ratings = socialRatingManager.GetRatings(profile);
foreach (SocialRating rating in ratings)
{
    if (rating.Rating > 0)
    {

         string title = rating.Title,
         int rating = rating.Rating;
    }


SharePoint 2010 Circulation list Confirm option is disabled when item is created programatically.

SharePoint 2010 Circulation list in "Group Work Site" template provides user with the ability to get confirmation or dis-confirmation from recipients without developing any kind of approval workflow. It provides recipients with Confirm and Disconfirm options in ribbon of display form.

There is one common mistake developer usually do while working with this list programatically.
Specially when you create a list item programatically, you use following code (Line numbers are included for reference):

1. SPList circulationList = SPContext.Current.Web.Lists["Circulation"];
2. SPListItem circulationItem = circulationList.AddItem();
3. circulationItem["Title"] = titleText;
4. circulationItem["Body"] = bodyText;
5. circulationItem["DueDate"] = DateTime.Today.AddDays(7);

6. circulationItem["Confidential"] = true;
7. circulationItem["AllowEditing"] = false;
8. SPFieldUserValueCollection recipients = new SPFieldUserValueCollection();
9. recipients.Add(new SPFieldUserValue(SPContext.Current.Web, user1.ID, user1.Name));
10. recipients.Add(new SPFieldUserValue(SPContext.Current.Web, user2.ID, user2.Name));

11. circulationItem["V4SendTo"] = recipients;
12. circulationItem.Update();

Problem: When recipient users view this list item, they cannot confirm this because both Confirm and Disconfirm option are disabled for them.

Solution: There are two hidden fields which are not even listed in the columns of List Settings page of this list.
  1. V4Confirmed: contains users who have confirmed this item.
  2. V4NotConfirmed: contains users who have disconfirmed this item.
When we create list item programatically we need to populate V4NotConfirmed field  with all the recipient users to enable Confirm option for them (SharePoint manages this automatically while creating circulation item from web interface).
Add following code before line 12.
circulationItem["V4NotConfirmed"] = recipients;
This will resolve the issue.

SharePoint 2010 SPContext.Current.Web.EnsureUser(LogonName); issue.

Microsoft's documentation says "SPContext.Current.Web.EnsureUser(LogonName); Checks whether the specified logon name belongs to a valid user of the website, and if the logon name does not already exist, adds it to the website.". But when you check same user with following code:
SPUser user = SPContext.Current. Web.Users.GetByID(userID);
user not found exception occurs if the user is never logged in to the site.

But you can find same user information in SPContext.Current.Web.SiteUserInfoList  using following code:

SPListItem user = SPContext.Current.Web.SiteUserInfoList.GetItemById(userID);
string name = user.Name;