Friday, April 30, 2010

Updating read-only fields with the Lists web service

This is a known limitation (from a user’s perspective that is, obviously Microsoft had a good reason to design it this way) – you cannot edit fields like the Created, Modified, Created By, Modified By, etc using the standard SharePoint Lists web service and its UpdateListItems method (Updated 2011-1-20 – the Created By (Author) and Modified By(Editor) fields can be updated in this manner only for non-library SharePoint lists). On the other hand using the object model with SPListItem.UpdateOverwriteVersion or SPWeb.ProcessBatchData you can easily modify the values of read-only fields. And back to the Lists web service – I recently found a small work-around to this issue – the idea is quite simple – if the problem is with read-only fields why just not change the ReadOnly attribute in the field’s schema and check to see whether this will work. I tested the schema changing and it turned out that the UpdateListItems method was now able to change the values of the modified fields. The next question is whether it is possible to make this field schema change with a web service – the answer is again yes – the Lists web service has the UpdateList method which allows among other things to modify the schema of the fields in the list. So, combining the two methods it is possible to first call UpdateList and change the fields whose values should be modified to non read-only, then call UpdateListItems and update the values in the list items and finally call UpdateList again to have the read-only attribute set back to the original value (check the sample code snippet below). Note here that setting the ReadOnly schema attribute back to TRUE is necessary otherwise you will see some side effects like the fields appearing in the new and edit forms of the list, the Modified field will stop reflecting the last modified date, etc. Another important note – if the field is sealed (its “Sealed” attribute is set to TRUE) – the Lists.UpdateList cannot update its schema so the whole approach is unusable in this case (unless you set the “Sealed” attribute to false with the object model or a tool which further complicates the issue).

And now several words about the disadvantages of this work-around (a little attempt to discourage you from using it) – let me start with the schema changes – it is obviously not natural to change the schema of the list (or its children) to get an update of its list items right, another thing is that these schema changes and reverts will become a real mess if you try to run several updates simultaneously. So my advice would be that this approach should be used only in a one-time batch updates and/or when there is really no alternative to using the standard SharePoint web services (no option for using the object model or to create and deploy a custom web service).

And here is a small sample code snippet that demonstrates this work-around:

        public void Test()

        {

            string webUrl = "http://myserver";

            string listName = "docs";

            Lists.ListsSoapClient listsClient = this.GetListsClient(webUrl);

 

            // 1st a call to Lists.GetList - we need the list's version - it is returned in the Version attribute

            XElement listData = XElement.Parse(listsClient.GetList(listName).OuterXml);

            string listID = listData.Attribute("ID").Value;

            string version = listData.Attribute("Version").Value;

            // in the updateFields parameter of Lists.UpdateList the full schema of the fields should be provided

            string updateFields = @"<Fields>

   <Method ID='1'>

      <Field ID='{28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f}' ColName='tp_Modified' RowOrdinal='0' ReadOnly='FALSE' Type='DateTime' Name='Modified' DisplayName='Modified' StorageTZ='TRUE' SourceID='http://schemas.microsoft.com/sharepoint/v3' StaticName='Modified' FromBaseType='TRUE' Version='4' ShowInNewForm='FALSE' ShowInEditForm='FALSE' />

   </Method>

   <Method ID='2'>

      <Field ID='{8c06beca-0777-48f7-91c7-6da68bc07b69}' ColName='tp_Created' RowOrdinal='0' ReadOnly='FALSE' Type='DateTime' Name='Created' DisplayName='Created' StorageTZ='TRUE' SourceID='http://schemas.microsoft.com/sharepoint/v3' StaticName='Created' FromBaseType='TRUE' Version='4' ShowInNewForm='FALSE' ShowInEditForm='FALSE' />

   </Method>

</Fields>";

            XmlDocument doc = new XmlDocument();

            doc.LoadXml(updateFields);

            // Lists.UpdateList: set fields to not read-only

            XElement result = XElement.Parse(listsClient.UpdateList(listID, null, null, doc.DocumentElement, null, version).OuterXml);

            // get updated version from the result XML - for the second call of Lists.UpdateList

            version = result.Elements().Where(el => el.Name.LocalName == "ListProperties").First().Attribute("Version").Value;

 

            // prepare the XML for the list item update

            string updateDates = @"<Batch OnError='Continue'>

  <Method ID='M0' Cmd='Update'>

    <Field Name='ID'>1</Field>

    <Field Name='FileRef'>/docs/zt.txt</Field>

    <Field Name='Modified'>2010-04-04T22:17:00Z</Field>

    <Field Name='Created'>2010-01-01T00:05:00Z</Field>

  </Method>

</Batch>";

 

            doc.LoadXml(updateDates);

            // Lists.UpdateListItems: update Created & Modified

            result = XElement.Parse(listsClient.UpdateListItems(listID, doc.DocumentElement).OuterXml);

 

            // revert the fields' schema

            updateFields = updateFields.Replace("ReadOnly='FALSE'", "ReadOnly='TRUE'");

            doc.LoadXml(updateFields);

            // Lists.UpdateList: set fields back to read-only

            result = XElement.Parse(listsClient.UpdateList(listID, null, null, doc.DocumentElement, null, version).OuterXml);

        }

The code demonstrates how to update the Created and Modified fields of a document library item (check the comments inside the code for more details). Note that this is a sample quality code and you shouldn’t use it directly without serious improvements – for example using a try-finally block with the second call to the Lists.UpdateList method placed in the “finally” block; adding an extra call to Lists.GetList to get a fresh value of the “Version” attribute of the list, just before the second list update, etc.

Thursday, April 15, 2010

SharePoint 2010 version of the WebPart Manager tool

I created a SharePoint 2010 version of the WinForm WebPart Manager tool that I demonstrated in my previous posting. Sources and binaries of the 2007 and 2010 versions are now available in codeplex.

wpmanager1

The 2010 version works by default with the new XsltListViewWebPart instead of the older ListViewWebPart. It also has the additional option to save the web parts’ ID-s when exporting a module element feature – this is necessary for web parts which are placed in “rich content” editable fields.

I also added a new node level in both versions – below ListViewWebPart-s (or XsltListViewWebPart-s for SharePoint 2010) the associated with the web part SPView object now appears.

Wednesday, April 7, 2010

SharePoint WebPart Manager tool

After the many WinForm tools that I’ve created using the TreeView & PropertyGrid combo (much like the SharePoint Manager 2007 tool), I finally decided to create a reusable core allowing various plug-ins for the tree hierarchy structure and for the commands available for the different nodes. And here’s the first tool based on this little framework of mine – the WebPart Manager tool (download):

WPManager1

WPManager2

As you see from the screenshots the tool displays a hierarchical view of all web applications, site collections and sites down to the folder structure of each site with all files and web parts in web part page files. The property grid is populated only for the web part level (this way the tool is much faster and the display options for the different levels are actually configurable in the accompanying configuration XML files of the tool). The tool supports the standard web part operations normally available through the standard SharePoint web UI – save, export, delete, close, open and move a web part and on the file node level another set of useful commands is exposed – starting with the file specific actions – check in/out, overwrite check-in, discard check-out, publish (these are really handy especially for publishing pages since you need to have the file checked out first before you can modify or add web parts to the page); to batch web part oriented commands like: export all web parts, import/add/delete web parts and some really nice goodies – export module feature and import web parts from module element file. The UI of the tool includes a common dialog interface with a dialog form using again the PropertyGrid control in conjunction with several custom UIEditor classes to capture the required parameters for the selected command (check the tool to get an impression of it).

And here is a short list of the available commands in the tool:

For the web part level:

  • Save web part – saves the selected web part after you have changed its properties in the PropertyGrid control.
  • Export web part – exports a .webpart or .dwp file with the web part data to the file system.
  • Delete web part – deletes the web part from the page.
  • Close web part – closes the web part.
  • Open web part – opens a previously closed web part (available only for closed web parts).
  • Move web part – allows you to change the web part zone and zone order of the web part.

For the file level:

  • Check out – checks out the selected file (available only for files in libraries and if the file is not already checked out)
  • Check in minor – checks in minor the selected file (available only for files in libraries and if the file is checked out)
  • Check in major – checks in major the selected file (available only for files in libraries and if the file is checked out)
  • Check in overwrite– checks in with overwrite the selected file (available only for files in libraries and if the file is checked out)
  • Discard check out – discards the check-out of the selected file (available only for files in libraries and if the file is checked out)
  • Publish – publishes the selected file (available only for files in libraries and if the file was previously checked in to a draft state)
  • Export all web parts – exports all web parts in the selected file to a specified folder (saving the web parts as .webpart and .dwp files)
  • Import web part(s) – imports web parts from .webpart and .dwp files from a selected folder in the file system (you can specify one or several files). You need to also specify the target web part zone ID on the page and optionally the zone index for the first web part to be imported (the consecutive parts will be assigned incrementing indices)
  • Add web part(s) – adds web parts from a list of available web parts for the current site – the list consists of all non-system SharePoint lists (resulting in adding a ListView web part) in the site and all web parts available in the web part gallery of the site. You need to also specify the target web part zone ID on the page and optionally the zone index for the first web part to be added.
  • Delete web part(s) – deletes web parts from the page – you can select to delete all web parts on the page or just select one or several of them to be deleted.
  • Export module feature – exports a module feature with the current file to a specified folder. It exports a feature.xml and an elements.xml file together with the file of the page. The elements file contains a Module element with a single File element containing the web part definitions of all web parts on the page. There is an option to export only the elements file instead of the full feature which is handy when you want to quickly copy all web parts of one page to another one using the “Import web parts from module file” command. For publishing pages the File element is exported with all relevant Property elements.
  • Import web parts from module file – imports web parts from an elements file (containing a Module element with one or many File elements) of an existing module feature or one created with the “Export module feature” command. The command shows a dialog which allows you to select the elements file to be imported and after the file is chosen a drop down list gets populated with the available File elements in the Module elements file so that you can choose which File definition to be imported. The command automatically resolves any resource tokens and ~Site/~SiteCollection tokens if present in the Module file.

For the folder level:

  • Export module feature – analogous to the command with the same name on the file level with the difference that you can choose whether to export all files in the current folder or just a group of selected files. The same command is available on the site level too – in this case it exports all/selected files from the root folder of the site.