Tuesday, January 25, 2011

Show reusable data on the master page in Sharepoint

When you create site collection based on OTB publishing portal template, root site contains Reusable Data list. It stores pieces of information (it can be plain or html formatted text) which can be shown on different pages in your portal. In order to show reusable data on the publishing page you need to create or edit existing page and when focus will be on the page content field (rich html field type) in the top ribbon you can choose Insert > Reusable data. It will insert reference on the reusable data list item on your page. So when you will change reusable data list item, it will affect all pages where it is inserted.

However, there can be situations when you need to show reusable data on the master page (i.e. not on publishing pages). It is not so straightforward, because after this you will notice that SPContext.Current.ListItem always contains reference on the publishing page which is shown with you master page. It means that all field controls on the master page will show field values from publishing page, but not from the master page.

In order to show field values from master page I created custom control, which retrieves field value from master page’s metadata and displays it:

   1: public class MasterPageFieldValue : Control
   2: {
   3:     protected override void Render(HtmlTextWriter output)
   4:     {
   5:         try
   6:         {
   7:             if (this.Page.Master == null)
   8:             {
   9:                 return;
  10:             }
  11:             var file = SPContext.Current.Site.RootWeb.GetFile(this.Page.MasterPageFile);
  12:  
  13:             var field = file.Item.Fields.Cast<SPField>().FirstOrDefault(f => f.InternalName == this.FieldName)
  14:             if (field == null)
  15:             {
  16:                 return;
  17:             }
  18:             output.Write(field.GetFieldValueAsHtml(file.Item[this.FieldName]));
  19:         }
  20:         catch (Exception x)
  21:         {
  22:             // log
  23:         }
  24:     }
  25: }

As you can see it gets SPListItem instance for the master page (which is located in Masterpage gallery (_catalogs/masterpage) in most cases) and retrieves field value from its metadata. After that it renders value to output. Notice that it inherits from standard ASP.Net Control class (i.e. not from Sharepoint BaseFieldControl as you may think it should use), because BaseFieldControl uses SPContext.Current.ListItem internally and as I said in our case it is not correct storage for retrieving metadata values.

In order to use this control on you need to add it on the master page by common way:

   1: <%@ Register TagPrefix="uc" Assembly="MyAssembly" %>
   2: ...
   3: <uc:MasterPageFieldValue FieldName="ReusableData" runat="server" />
After that you need to add “ReusableData” field into Publishing Master Page content type (or another content type you use for your master page). Then go to Masterpage gallery (_catalogs/masterpage) and choose Edit properties for your master page. Insert reference on reusable data list item by regular way and check in changes. After this you will have reusable data shown on the master page.

Monday, January 17, 2011

Submit html form using Content Editor Web Part in Sharepoint

Recently I faced with the problem of submitting of html form which should be added on the page using CEWP. The problem is that in most cases master page of Sharepoint web part already has top-level html form:

   1: <form runat="server" onsubmit="return _spFormOnSubmitWrapper();">
   2: ...
   3: </form>

And all placeholders are inserted within this form. So if you try to add your form in page layout or add html layout with form element on the page using standard Content Editor Web Part you will get nested forms which is not well formed html.

Trying to figure out how to solve this issue, I found the following project on Codeplex: http://sharepointformsubmit.codeplex.com. This is single javascript file (jquery.SharePointFormSubmit.js) which uses the following trick: instead of real html form you should add div element in the CEWP. And instead of submit input element you should use button with onclick event handler attached. Script will create form with all input elements dynamically, but not as nested form – as a sibling form – and then will call submit() method on it.

Unfortunately it did work for me as is (at least revision 1 from 2009-04-16 which is available on Codeplex. There is also revision 2 available on code.google.com - http://code.google.com/p/jquerysharepointform, but I didn’t tried it). I was needed to slightly modify the script:

   1: (function($){
   2:     $.fn.SharePointFormSubmit = function(element,method,action) {
   3:         var e; // new input element
   4:         var f = document.createElement("form"); // form
   5:             f.method = method;
   6:             f.action = action;            
   7:             f.setAttribute("style","display:none");
   8:  
   9:             $(element).find("input, select, textarea").each(function(){
  10:                 e = document.createElement("input")
  11:                 e.setAttribute("type", $(this).attr("type"));
  12:                 e.setAttribute("id", $(this).attr("id"));
  13:                 e.setAttribute("name", $(this).attr("name"));
  14:                 e.setAttribute("value", $(this).val());
  15:                 e.setAttribute("checked", $(this).attr("checked"));
  16:                 e.setAttribute("multiple", $(this).attr("multiple"));
  17:                 f.appendChild(e);
  18:             });
  19:  
  20:             var s = document.body.appendChild(f);
  21:             s.submit();
  22:     };
  23: })(jQuery);

Having this script you need to do the following actions in order to submit form from CEWP:

1. Add 1st CEWP on the page with the following content:

   1: <script type="text/javascript"
   2: src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
   3: <script type="text/javascript"
   4: src="http://jquerysharepointform.googlecode.com/files/jquery.SharePointFormSubmit.js">

You can also download these scripts on your web server (e,g, in [12 or 14]/Templates/Layouts folder) and use them from it.

2. Add 2nd CEWP with your “form” (instead of form you need to insert div element, like described here):

   1: <div id="divIdForm">
   2: Email: <input type="text" name="email" id="email"><br />
   3: <input type="submit" value="Submit" onclick="jQuery().SharePointFormSubmit('#divIdForm', 'post',
   4: 'http://example.com')">
   5: </div>

In this example with this code added to the CEWP you will be able to submit forms into “html://example.com” URL. It will work also for cross domain submits.

Monday, January 10, 2011

What to do if Take snapshot option is disabled in VMWare

We often use virtual environments for Sharepoint development (VMWare and VirtualPC are most frequently used virtualization platforms in our case). I also often use snapshot feature of VMWare if some risky configuration changes are required. It is really useful when you can rollback system changes to the last stable snapshot in a few minutes comparing with spending hours on restoring failed system from scratch.

Recently I encountered with strange situation: “Take Snapshot…” option in menu was disabled (grayed):

image

After some investigation I found that hard disk drives were configured in Independent persistent mode (VM > Settings > Hard disk > Advanced):

image

As documentation says:

Independent – For independent disks, the data on the disk is not recorded when you take a snapshot of the virtual machine. If the Independent check box is unavailable, it might mean that the virtual machine currently has snapshots. After you delete the snapshots, the check box becomes available.

I had several HDD – all with independent mode. So I uncheck “Independent” checkbox for all drives, and after that “Take Snapshot…” option became available:

image

It is important to note that you should uncheck “Independent” checkbox for all disks, because if there will be at least one disk in independent mode snapshots will be still disabled. Hope it will economy your time if you will face with similar problem.