Tuesday, December 27, 2011

How to hide properties in Document Information Panel – SharePoint

Document Information Panel – This is the window which shows all the properties associated with the document. Let it be out of the box / default properties (like Title, Keywords), or custom properties or content type properties.

Wait, what are content type properties?
Consider a scenario where you have a SharePoint site and a document library. You create some site columns and then create content type for your document library. (Inheriting from the document content type). Now you associate this newly created content type to your document library and done.

You go ahead and click on create New Document link and Word Editor opens up. Now there you see the Document Information Panel (DIP) containing all the site columns in your content type. These are metadata holders for your document. And that’s why these document properties are called as content type properties.

In Following Image, I have opened the document from the SharePoint document library and added Version and Comments column in the default document content type , which are visible in DIP.


Issue:
There were some of the content type properties which were getting displayed in DIP, but I didn’t want users to see those /change those. In short I needed to hide those.
Let’s try to hide version property from DIP.
Solution:
First thought comes to mind like – ok these properties are nothing but the site columns right? So why don’t we just delete the site columns and get rid of properties in DIP? But NO. Because if we delete the site columns from the content type , the DIP won’t show the property but document will not be holding the metadata for that column.
So what could be done?
Trick I found is, just hide the site column.
How we can do it?
Library settings > Find your content type and click on it > select the site column which you don’t want to see in DIP > click on Hidden option in advanced option and select ok.



Now DIP will not show the content type property when creating new documents from the library
Version column gets hidden from DIP



Note: This approach is used only when the site column is not of type mandatory/required. As per my knowledge you cannot hide the column which is required.


How to debug the word add in project in Visual Studio

Last few days I was playing much with MS word add ins and its development, Yes you are right, when the word ‘develop’ comes into the picture , each and every developer needs to think some logic and then they let the figures do the remaining job. There are many times when run-time error occurs and developer go ahead and track/find them using debugger.
Most of us use Visual Studio, the developer’s heaven as IDE for the .NET based application development and Microsoft has given way too good features to debug your code. (Link here)
Some examples:
·         When debugging web based projects / SharePoint farm solutions , one attaches the debugger to the w3wp.exe (IIS process)
·         When debugging sandboxed solutions one needs to attach the debugger to the SPUCWorkerProcess.exe (SPUC)
·         And again while debugging the timer jobs in SharePoint debugger needs to be attached to Timer service. (OWSTimer.exe)
Ok ok, before I get the question like – come on we know all of these.. I will come to the point directly.
I learnt a lesson that if you need to debug the MS Office Addins then you must attach the debugger to the appropriate MS Office Client.
In my case, as I was working with MS Word Addin – so I needed to attach the debugger to WINWORD.exe service, like this

 
If you do not see WINWORD.exe process , make sure that you have launched the MS Word Client and selected – Show all process , show process from all users/sessions in the attach debugger window.
Also If the debugger is not getting attached, then make sure that the add-in is referring to the dll is latest in the respective location. (For example If add in is referring to the dll somewhere in release folder of your solution then make sure that the dll is latest)

Wednesday, November 16, 2011

How to add web part on SharePoint 2010 Wiki Pages / On Rich Content Programmatically


While working with SharePoint 2010 one of my fried came across a scenario where he needed to modify the home page of team site using a feature. Feature should delete the existing content in out of the box team site’s home page and add new web parts.

Though this sounds very simple in first shot but practically little tricky, that’s what I experienced.
SharePoint 2010 has come up with new features and one of them is modification in team sites. If you observe that when a new out of the box team site is created, there is no default.aspx instead default page is replaced by Home.aspx. Home.aspx is a wiki page and resides in the Site Pages library of team site.
As this is a wiki page so it’s good that this supports direct editing and you can add web parts directly in the rich text area using UI. But when it comes to adding web parts to this page programmatically then problem begins.

Waldek Mastykarz has given excellent workaround to this problem where he has shown how to add a web part in Publishing Page Content of publishing page.  We can use similar technique to add web parts to wiki page as well, only difference is of field name of wiki page.
In the example I have added two list view web parts in Home.aspx page , one reside at left and other one is at right of page. This can be done with simple HTML markup where <td> and <tr> are defined with certain widths.

In my case Ultimate output should be like this HTML
//<div class=\"ms-rtestate-read ms-rte-wpbox\" contentEditable=\"false\">
//  <table id=\"layouts table\" style=\"width:100%\">
//      <tr style=\"vertical-align: top;\">
//          <td style=\"width=66.6%\">
//              <div class=\"ms-rtestate-read {0}\" id=\"div_{0}\"></div>
//          </td>
//          <td style=\"width=33.3%\">
//              <div class=\"ms-rtestate-read {1}\" id=\"div_{1}\"></div>
//          </td>
//      </tr>
//  </table>
//</div>

In example above {0} and {1} would be replaced by GUIDs of web parts.
I have tried this with console application for POC but one can add this in feature receiver.



using (SPSite site = newSPSite("http://yoursite/"))
{
using (SPWeb _web = site.OpenWeb("/yourteamsite"))
   {
SPList _list1 = _web.Lists.TryGetList("List1");
SPList _list2 = _web.Lists.TryGetList("List2");
SPList _sitePages = _web.Lists.TryGetList("Site Pages");

SPFile _file = _web.GetFile(_web.Url + "/SitePages/Home.aspx");

if (_list1 != null&& _list2 != null)
     {
XsltListViewWebPart _list1View = newXsltListViewWebPart();
       _list1View.ListId = _list1.ID;
       _list1View.ViewGuid = _list1.DefaultView.ID.ToString("B").ToUpper();
       _list1View.XmlDefinition = _list1.DefaultView.GetViewXml();

XsltListViewWebPart _list2View = newXsltListViewWebPart();
       _list2View.ListId = _list2.ID;
       _list2View.ViewGuid = _list2.DefaultView.ID.ToString("B").ToUpper();
       _list2View.XmlDefinition = _list2.DefaultView.GetViewXml();

if (_file != null)
       {
using (SPLimitedWebPartManager _mgr = _web.GetLimitedWebPartManager(_file.Url, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared))
         {
Guid storageKey1 = Guid.NewGuid();
string wpId = String.Format("g_{0}", storageKey1.ToString().Replace('-', '_'));
          _list1View.ID = wpId;

Guid storageKey2 = Guid.NewGuid();
string _liWPID = String.Format("g_{0}", storageKey2.ToString().Replace('-', '_'));
           _list2View.ID = _liWPID;
           _list2View.ID = _liWPID;

//Deleting Web Parts
for (int i = _mgr.WebParts.Count - 1; i > 0; i--)
          {
            _mgr.DeleteWebPart(_mgr.WebParts[i]);
          }

//Adding Web Part
          _mgr.AddWebPart(_list1View, "wpz", 0);
          _mgr.AddWebPart(_list2View, "wpz", 1);

//Deleting Wiki Content
           _file.Item[SPBuiltInFieldId.WikiField] = string.Empty;
 _file.Item.UpdateOverwriteVersion();

string marker = String.Format(CultureInfo.InvariantCulture, "<div class=\"ms-rtestate-read ms-rte-wpbox\" contentEditable=\"false\"><table id=\"layouts table\" style=\"width:100%\"><tr style=\"vertical-align: top;\"><td style=\"width:66.6%\"><div class=\"ms-rtestate-read {0}\" id=\"div_{0}\"></div></td><td style=\"width:33.3%\"><div class=\"ms-rtestate-read {1}\" id=\"div_{1}\"></div></td></tr></table></div>", storageKey1.ToString("D"), _ storageKey2.ToString("D"));


_file.Item[SPBuiltInFieldId.WikiField] = marker;
            _file.Item.UpdateOverwriteVersion();

Console.WriteLine("done");
          }
}

        }

       }
      }

Console.ReadLine();
   }

Working with SharePoint Multilingual Titles Programatically

Last week I was doing some R&D on SharePoint Multilingual user interface and hence thought to share some learning experience which I got.


Background:
SharePoint 2010 ships with the new feature called Multilingual User Interface, which is helpful for displaying the content in the web site in various languages depending upon user selection. I will not dive in to the much details of it as many of good posts are available on the same. Some examples can be found Here and Here.
Just a basic rule of this feature is, it works on the UI culture of the current thread, and shows content to user in different languages.

Problem:
So far so good, SharePoint 2010 manages to translate all link titles, list, libraries titles, site columns titles, content type’s titles and many more things which are out of the box. So when user selects a different language in MUI selector menu then he or she is able to see the OOB translated content very well , but what happens when we have our custom lists, list web parts, custom site columns, content types ? Those don’t get translated by default.

Solution:
One has said very well, there is always a solution J and this line cheers me up. So to overcome this kind of scenario there are two ways.

1.  Manual Approach: one has to go to each and every created custom lists, site columns, and content types and update titles of the same in different languages by changing languages every time using MUI selector. Though this sounds easy but takes lots of efforts when you have sites already provisioned and there is lots of such custom data.

2. Programmatic Approach: I always love such approaches and so I always try to find way to do them as they makes life easy by just few clicks.

A property “TitleResource” is available now with some SharePoint objects which is of type SPUserResourceclass, with which you can get or set the titles of webs, lists, site columns, content types for multiple language cultures.
Following example shows that how we can get or set the multilingual values of a site’s title for German UI culture.



using (SPSite site = newSPSite("http://YourSite"))
{
using (SPWeb _web = site.OpenWeb())
  {
Console.WriteLine(string.Format("{0}-{1}", "Title", _web.Title));

SPUserResource _userResource = _web.TitleResource;
if (_userResource != null)
    {
       _web.AllowUnsafeUpdates = true;
       _userResource.SetValueForUICulture(newSystem.Globalization.CultureInfo(1035), "German Title");
       _userResource.Update();
       _web.AllowUnsafeUpdates = false;

Console.WriteLine("{0}-{1}-{2}", "Updated Title for Culture", "German", _userResource.GetValueForUICulture(newSystem.Globalization.CultureInfo(1035)));

}
  }
}
Console.ReadLine();



Monday, October 31, 2011

Received Microsoft Community Contributor Award


Yesterday Night , I opened my emails and happen to see the Email From Microsoft mentioning that,

Dear Bhushan, 
Congratulations! We’re pleased to inform you that your contributions to Microsoft online technical communities have been recognized with the Microsoft Community Contributor Award. 

I am really thankful to Microsoft for recognizing my efforts and awarding me with such valuable award.

Here is the Certificate J


Saturday, October 1, 2011

SharePoint Localization in Sandbox Environment


Last week I was working with localization in SharePoint and just wanted to share some thoughts. We all know that this can be Managed with the Resource files and at run time we can read the values based on the culture (either UI culture or your site’s culture) and reosurce files are kept in the 14 hive of SharePoint Server file system.

I will not dive in to the details of the how to do localization in SharePoint but I can point out some references to do this , like this post of John Powell and also this MSDN post, explained in excellent way.

So far so good, but when we work with sandbox environment then we cannot access the file system of the server and so puzzle is where and how to keep this resource files?
Well but that didn’t take too much time to solve as MSDN has already given a very good documentation to take on this issue.
Here is link on how to do this : MSDN – Localization in Sandbox environment and this post which is a good pictorial explanation.

Some Quick Notes :

What are satellite assemblies? – Satellite assemblies are the compiled libraries which contains the localizable resources while creating multi lingual applications. So a satellite assembly per language / culture. – Reference Here

So where these satellite assemblies are placed in Sandbox solutions ? – when Sandbox solution with Satellite assembies is deployed then everything is packaged as wsp and deployed to site collection’s Solutions gallery. Reference Here

Friday, August 26, 2011

Working with SharePoint Ratings – Part 2

How to enable and disable SharePoint 2010 rating programmatically

If you have referred the details in the previous post on Working with the SharePoint 2010 Rating – Part 1 for theoretical explanation on what happens in the background, now let’s do something interesting in Visual Studio.
When you visit the list settings page and click on rating settings link , you gets redirected the application page called as “RatingSettings.aspx” with the query string as the list GUID and on this page you can decide the setting about enabling and disabling ratings on that list.
I didn’t see any other way to achieve this with programmatic approach in the server object model in SPList class and so for curiosity I opened up the reflector to see what Microsoft has done.
And so here are the ways to programmatically enable the ratings on list
1.       Using the reflection
typeof(Microsoft.SharePoint.Portal.RatingsSettingsPage).
GetMethod("EnableRatings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).
Invoke(null, new object[] { _objectofSPList, true });

There is a method “EnableRatings” in class “RatingSettingsPage” and method is internal and static. So I am just invoking the method here. This method takes two parameters.
a.    object of SPList on which you need to enable the ratings
b.    a Boolean value (true/false) – keep true to prorogating rating on list items
There is one more method of this class, which allows us to disable the rating
typeof(Microsoft.SharePoint.Portal.RatingsSettingsPage).
GetMethod("DisableRatings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).
Invoke(null, new object[] { _objectOfSPList });

This method accepts a single parameter which is as the SPList and this is the target list.
2. Using the actual code
      Rating can also be enabled without using the reflection, but in this case you write much more code than in previous method.
This method is used for enabling the rating settings on a list. You can see that there are two fields which are being add in the list.
  private void EnableRatingSetting(SPList _list)
  {
    SPFieldCollection _allfields = _list.Fields;
    SPFieldCollection _availFields = _list.ParentWeb.AvailableFields;
    if (!_allfields.Contains(FieldId.AverageRatings))
    {
       SPField field = _availFields[FieldId.AverageRatings];
_list.Fields.AddFieldAsXml(field.SchemaXmlWithResourceTokens, true, SPAddFieldOptions.AddFieldToDefaultView | SPAddFieldOptions.AddFieldInternalNameHint | SPAddFieldOptions.AddToAllContentTypes);
    }
    if (!_allfields.Contains(FieldId.RatingsCount) && _availFields.Contains(FieldId.RatingsCount))
    {
      SPField field2 = _availFields[FieldId.RatingsCount];
      _list.Fields.AddFieldAsXml(field2.SchemaXmlWithResourceTokens, false, SPAddFieldOptions.AddFieldInternalNameHint | SPAddFieldOptions.AddToAllContentTypes);
    }
      _list.Update();

   }

And this is the method to propagate the changes to the list items.
There is a method available with the SocialRatingManager class called as PropogateRating which takes care of this.
private void PropogateChanges(SPList _list)
{
SocialRatingManager _socialRatingMgr = new   SocialRatingManager(SPServiceContext.Current);
   string _baseUrl = _list.ParentWeb.Url;
   if (_baseUrl.EndsWith("/", StringComparison.OrdinalIgnoreCase))
   {
      _baseUrl = _baseUrl.TrimEnd(new char[] { '/' });
   }
   foreach (SPListItem item in _list.Items)
   {
     string _itemUrl = item.Url;
     if (_itemUrl.StartsWith("/", StringComparison.OrdinalIgnoreCase))
     {
        _itemUrl = _itemUrl.TrimStart(new char[] { '/' });
     }
     SPSecurity.RunWithElevatedPrivileges(delegate()
     {
        _socialRatingMgr.PropagateRating(new Uri(_baseUrl + "/" + _itemUrl));
     });
    }

   }

   private static SPField GetField(Guid id, SPFieldCollection fieldColl)
   {
      return fieldColl[id];
   }

This method is used to disable the rating on a list
private void DisableRatingSetting(SPList _list)
    {
       SPField _field1 = GetField(FieldId.AverageRatings, _list.Fields);
       if (_field1 != null)
       {
         _list.Fields.Delete(_field1.InternalName);
       }
       SPField _field2 = GetField(FieldId.RatingsCount, _list.Fields);
       if (_field2 != null)
       {
          _list.Fields.Delete(_field2.InternalName);
       }
        _list.Update();

    }

Thursday, August 25, 2011

Working with SharePoint Ratings – Part 1

Recently I was doing some R&D with the SharePoint Ratings and so thought to share my thoughts.
As we know that with the launch of SharePoint 2010 version, we got new social features like content tagging and content rating. For content ratings, we can rate the different contents in the list and libraries in range of 0 to 5.
For example: I have a publishing site and the pages library, so I need to have functionality such that the end users should be able to rate the pages inside the pages library.
How to do this?
For any list or library, there is a link to set the rating capability. When you go to the List and then in its settings, there you can see the link named as “Rating Settings”. After you click on that, a page opens where you can enable and disable this setting.
Ok so far so good. ? But what happens in the background?
Well, I have observed that when you enable ratings on any list or library, the two Site Columns/Fields gets added to the SharePoint list. Obviously those get added to hold this rating as a metadata.
Which are those fields?
Field names are:
    Display Name                          Internal Name                                        GUID
1. Rating (0-5)                           AverageRating               {5a14d1ab-1513-48c7-97b3-657a5ba6c742}        
2. Number of Ratings                  RatingCount                   {b1996002-9167-45e5-a4df-b2c41c6723c7}         

And here is the screen shot after enabling the rating on pages list



Rating is the asynchronous event and we talk about social features, they gets saved in social database which gets created when you have activated the user profile service application. So in short, to use rating you should have a user profile service running in your farm.
There is one timer job “User Profile Service Application – Social Rating Synchronization” job which by default runs after every hour and aggregates all the ratings.


Why ratings disappear after page refresh?
I was also puzzled by a question that when we try to rate a page in pages library and then refresh the page, ratings given were disappearing. Why?
So answer is the Timer Job. As this job runs every hour, so unless and until the job has run and aggregated the ratings, we don’t see the ratings which were given. So I changed the job schedule to run every minute to have immediate effects.
But wait, there might be the case that If you have provisioned all the sites hierarchies very fine in the production environment and now request is to enable the ratings on all the lists and libraries in each site and subsites. Of course not manually right? So How to do that?
Yes you are right, you can write a feature, or a console application or any other approach which will enable rating setting recursively on each list and library within the site.
But again question now is how to do this programmatically?
I will be posting this in part 2 of this post.

Monday, August 22, 2011

Hide Web Part’s Default Tool Part or Hiding Appearance from Web Part Properties

If the user wants to hide the default properties of a web part or some sections in the default tool part of a web part. How to do this? Here are the approaches.
I created a simple web part inheriting from SharePoint Web Part class So according to me, there can be two ways to do this.
(Microsoft.SharePoint.WebPartPages.WebPart) which has only a text box as user interface.
1.    Hide entire default tool part of a web part – this is pretty simple
2.    Hide specific sections in the default tool part. Example: hide only the appearance section in the default tool part.
And here are my approaches –
1.    Hiding the entire default tool part is very easy thing to achieve. When you override the GetToolParts method of the web part then you don’t have to add the default tool part in the array of ToolPart.
So sample can be written like:
Note that WebPartToolPart object is commented and not added in the array.
With this approach, you can only show the custom tool part for a web part.
public override Microsoft.SharePoint.WebPartPages.ToolPart[] GetToolParts()
{
  ToolPart[] _toolparts = new ToolPart[1];
  try
  {
    //WebPartToolPart _wptp = new WebPartToolPart();
    CustomToolPart _custom = new CustomToolPart();
     //_toolparts[0] = _wptp;
     _toolparts[0] = _custom;

  }
  catch (Exception ex)
  {
               
  }
  return _toolparts;
}

2.    Hiding the Specific Section of default tool part
Suppose we don’t want to hide the default tool part entrirely but need to hide specific sections like appearance.
so this approach shows that how we can achieve this.
when you are in your custom TooPart class and inside then this.Parent represents the ToolPart of a web part. If you try to do this.Parent.Controls then you can see that you get collection of controls inside the ToolPart pane. so we can easily set the visibility of those controls like
Note that you can add this code in CreateChildControls of your tool part class

if (Parent != null && Parent.Controls != null && Parent.Controls[1] != null)
{
  if (Parent.Controls[1].Controls != null)
  {
     Parent.Controls[1].Controls[0].Visible = false;
   }
}

Please Note that I have not tried these methods when web part is derived from ASP.NET Web Part class i.e. System.Web.UI.WebControls.WebParts.WebPart , If I get to know some update on that then I will update this post.

Here is my entire sample code

public class HiddenPropertiesWebPart : Microsoft.SharePoint.WebPartPages.WebPart
{

  public override Microsoft.SharePoint.WebPartPages.ToolPart[] GetToolParts()
  {
    ToolPart[] _toolparts = new ToolPart[2];
    try
    {
      WebPartToolPart _wptp = new WebPartToolPart();
      CustomToolPart _custom = new CustomToolPart();
      _toolparts[0] = _wptp;
      _toolparts[1] = _custom;

    }
    catch (Exception ex)
    {
               
    }
    return _toolparts;
 }

 protected override void CreateChildControls()
 {
   try
   {
     base.CreateChildControls();
     TextBox _txtBox = new TextBox();
     this.Controls.Add(_txtBox);
   }
   catch (Exception ex)
   {
     this.Controls.Add(new LiteralControl(string.Format("{0}-{1}", "Error", ex.Message)));
   }
  }
}

public class CustomToolPart : ToolPart
{
  protected override void CreateChildControls()
  {
    try
    {
      base.CreateChildControls();
      DropDownList _ddl = new DropDownList();
      _ddl.Items.Add("Item1");
      _ddl.Items.Add("Item2");
      this.Controls.Add(_ddl);

      //Hiding the Desired Section in the Default ToolPart

      // Parent.Controls[1] shows - default tool part
      // Parent.Controls[1].Controls[0] shows - default tool part's first control - that is Appearance
      if (Parent != null && Parent.Controls != null && Parent.Controls[1] != null)
      {
        if (Parent.Controls[1].Controls != null)
        {
          Parent.Controls[1].Controls[0].Visible = false;
        }
      }
     }
     catch (Exception ex)
     {
       this.Controls.Add(new LiteralControl(string.Format("{0}-{1}", "Error", ex.Message)));
     }
    }
}