Users can’t edit document in SharePoint 2013 with Word 2013

There are so many reasons that can prevent users from editing a document. If you are experiencing the same symptoms and have the same environment as described below then you are probably having the same problem and my solution should work for you too.

Case and Environment

  • SharePoint 2013 (build 15.0.4667.1000)
  • Word 2013, 32 bit (build 15.0.4615.1000)
  • Users can open and edit documents with Word 2010 without any problem
  • Users can open documents as read only but they can’t edit documents in Word 2013. Word 2013 hangs and the process must be killed.
  • IMPORTANT!: The document library or the content type has a choice field with only number choices (relatively big numbers)

Troubleshooting

After some time lost in troubleshooting (checking form another PC, inspecting the document content, checking permissions, inspecting the traffic with Fiddler,…) I didn’t find anything. Then I started removing columns from the content type one by one and testing the result. As soon as I removed one of the choice columns the problem was gone. Obviously Word 2013 is having troubles with SharePoint choice fields. That choice field had 15 choices and all were big numbers (2000,2001,…,2014). I added the choice field back and started to experiment with different range of consecutive numbers and got the following results:

Choice Values Edit in Word 2013
49,50,…,60 OK
49,50,…,62 OK
49,50,…,64 Failed
157,158,…,164 OK
154,155,…,164 Failed
1162,1163,1164 OK
1560,1561,1562,1563,1164 Failed

From the results we see that Word 2013 has more problems with bigger numbers, it can handle 14 choices with smaller numbers (sequence 49,50,..,62) but it can only handle 3 values with bigger numbers (sequence 1162,1163,1164).
The results presented above probably depend on the available resources for Word 2013, so on another more powerful PC it would probably be able to handle more choice values.
I can’t be sure what Word 2013 is trying to do when it has only number choices but we can add a text choice so Word 2013 treats them all as text. As soon as a text choice is added to the field Word 2013 works fine.
In my case I fixed it by adding a text choice “NA” so now the choice field has the following choices “NA”,”2000″,”2001″,…,”2014″.

Solution

Since Word 2010 is working fine with the same SharePoint choice field its clear that this is a Word 2013 issue, a bug we hope will be fixed with future updates.
As a workaround the problem can be fixed by adding at least one text choice value for the choice field which has only number choices.

User field in Event Receivers when using Claims based authentication and Classic mode authentication

When accessing a user field in event receivers there are few differences in the returned values when Classic mode authentication is used from the value when Claims based authentication is used. The difference is present in AfterProperties of ItemAdding and ItemUpdating event. This is true for SharePoint 2010 and SharePoint 2013 and its present only for custom lists but not for document libraries. I’ve done some testing and the results are presented in this post.

Testing

For testing I created a custom list that contains one User field, added the event receiver and overrode all the list item event handlers. For every event I tried to get the value of the user field from properties.BeforeProperties, properties.AfterProperties and properties.ListItem. The test is performed when adding/changing/deleting the item’s user field using the UI (New and Edit form) and when adding/changing/deleting the field pragmatically. The test is repeated when using Classic authentication and when using Claims based authentication. Also the test is repeated for SharePoint 2010 and SharePoint 2013.

Test results

From the results it turns out that when getting the value of a user field from AfterProperties in ItemAdding and ItemUpdating events the value is different when using the New and Edit form from the value when adding and updating the field pragmatically. This difference is present only when using Claims based authentication.
Below are shown the results only for the events that have this problem. At the end of this post you can find the complete test results for SharePoint 2013. In the tests user1 and user2 are used and they happen to have ID of 41 and 42.

Results when using claims based authentication :

When adding, modifying and deleting item using the UI:

Event From Value
ItemAdding AfterProperties -1;#i:0#.w|domain\user1
ItemUpdating AfterProperties -1;#i:0#.w|domain\user2

When adding,modifying and deleting an item programatically:

Event From Value
ItemAdding AfterProperties 41
ItemUpdating AfterProperties 42
Results when using classic authentication:

When adding,modifying and deleting an item using the UI:

Event From Value
ItemAdding AfterProperties 41
ItemUpdating AfterProperties 42

When adding,modifying and deleting an item programatically:

Event From Value
ItemAdding AfterProperties 41
ItemUpdating AfterProperties 42

You can see that the problem is only with claims based authentication while with classic authentication the results are the same when the item is added/modified using the UI and when it’s added/modified programmatically.
One small difference between SharePoint 2010 and SharePoint 2013 is that in SharePoint 2010 even if the user field is not changed the AfterProperties in ItemUpdating event will have the value “-1;#i:0#.w|domain\user1”. In SharePoint 2013 if the field is not changed then the AfterProperties in ItemUpdating event contain the user ID.

Consequences

Because of these differences it’s possible that your old code which has worked fine with classic authentication will not work with claims based authentication.
For example with classic based authentication in ItemAdding and ItemUpdating events the modified value of a user field can be retrieved as below:

 SPUser user = new SPFieldUserValue(properties.Web, properties.AfterProperties["UserField"].ToString()).User;

This code will not work when using Claims based authentication because the value of properties.AfeterProperties[“UserField”] is “-1;#i:0#.w|domain\user1”

Solution

Because its happening only when adding and modifying a user field using the New and Edit form it seems that the problem is with the PeapleEditor control. For some reason it doesn’t work as well with claims based authentication as it does with classic authentication.
To solve the problem I changed my code for getting the user from AfterProperties so it would work for Claims authentication no matter is the field changed using the PeapleEditor control or programmatically.

public static SPUser GetUserFromAfterProperties(object property, SPWeb web)
{
	SPUser user = null;
	string fieldValue = property == null ? null : property.ToString();

	//If adding/editing item directly in the list using NewItem/EditItem form
	if (fieldValue.Contains(";#"))
	{
		string loginName = fieldValue.Contains(";#") ? fieldValue.Split(new string[] { ";#" }, StringSplitOptions.None)[1] : null;
		user = web.EnsureUser(loginName);
	}
	//If adding/editing item programatically
	else
	{
		user = new SPFieldUserValue(web, poperty.ToString()).User;
	}

	return user;
}

To get the user from after properties call the above function in your event receiver:

SPUser user = GetUserFromAfterProperties(properties.AfterProperties["YourUserField"], properties.Web);

All test results

For reference below are all the results from the tests for SharePoint 2013 for Claims based authentication and Classic authentication, both when working with a list item using the UI and programmatically.

Resluts when using claims based authentication:

When adding, modifying and deleting item using the UI:

Event From Value
ItemAdding AfterProperties -1;#i:0#.w|neodomain0\u1
ItemAdding BeforeProperties N/A
ItemAdding ListItem ListItem = null
ItemAdded AfterProperties 41
ItemAdded BeforeProperties N/A
ItemAdded ListItem 41;#u1
ItemUpdating AfterProperties -1;#i:0#.w|neodomain0\u2
ItemUpdating BeforeProperties N/A
ItemUpdating ListItem 41;#u1
ItemUpdated AfterProperties 42
ItemUpdated BeforeProperties N/A
ItemUpdated ListItem 42;#u2
ItemDeleting AfterProperties N/A
ItemDeleting BeforeProperties N/A
ItemDeleting ListItem 42;#u2
ItemDeleted AfterProperties N/A
ItemDeleted BeforeProperties N/A
ItemDeleted ListItem ListItem = null

When adding,modifying and deleting an item programatically:

Event From Value
ItemAdding AfterProperties 41
ItemAdding BeforeProperties N/A
ItemAdding ListItem ListItem = null
ItemAdded AfterProperties 41
ItemAdded BeforeProperties N/A
ItemAdded ListItem 41;#u1
ItemUpdating AfterProperties 42
ItemUpdating BeforeProperties N/A
ItemUpdating ListItem 41;#u1
ItemUpdated AfterProperties 42
ItemUpdated BeforeProperties N/A
ItemUpdated ListItem 42;#u2
ItemDeleting AfterProperties N/A
ItemDeleting BeforeProperties N/A
ItemDeleting ListItem 42;#u2
ItemDeleted AfterProperties N/A
ItemDeleted BeforeProperties N/A
ItemDeleted ListItem ListItem = null
Results when using classic authentication:

When adding,modifying and deleting an item using the UI:

Event Properties Result
ItemAdding AfterProperties 8
ItemAdding BeforeProperties N/A
ItemAdding ListItem ListItem = null
ItemAdded AfterProperties 8
ItemAdded BeforeProperties N/A
ItemAdded ListItem 8;#u1
ItemUpdating AfterProperties 9
ItemUpdating BeforeProperties N/A
ItemUpdating ListItem 8;#u1
ItemUpdated AfterProperties 9
ItemUpdated BeforeProperties N/A
ItemUpdated ListItem 9;#u2
ItemDeleting AfterProperties N/A
ItemDeleting BeforeProperties N/A
ItemDeleting ListItem 9;#u2
ItemDeleted AfterProperties N/A
ItemDeleted BeforeProperties N/A
ItemDeleted ListItem ListItem = null

When adding,modifying and deleting an item programatically:

Event Properties Result
ItemAdding AfterProperties 8
ItemAdding BeforeProperties N/A
ItemAdding ListItem ListItem = null
ItemAdded AfterProperties 8
ItemAdded BeforeProperties N/A
ItemAdded ListItem 8;#u1
ItemUpdating AfterProperties 9
ItemUpdating BeforeProperties N/A
ItemUpdating ListItem 8;#u1
ItemUpdated AfterProperties 9
ItemUpdated BeforeProperties N/A
ItemUpdated ListItem 9;#u2
ItemDeleting AfterProperties N/A
ItemDeleting BeforeProperties N/A
ItemDeleting ListItem 9;#u2
ItemDeleted AfterProperties N/A
ItemDeleted BeforeProperties N/A
ItemDeleted ListItem ListItem = null

Get all sites of a specific template with PowerShell

The following script gets all the sites of a specific template in a web application and writes the URLs on the screen and saves them in a text file. In this example it searches for all team sites (WebTemplateId=1). To get the sites of another OOTB or custom template use the ID or the name of that template.

All sites are checked no matter the level because SPSite.AllWebs gets the collection of all Web sites that are contained within the site collection, including the top-level site and its subsides.

Add-PSSnapin Microsoft.SharePoint.PowerShell –erroraction SilentlyContinue

$webTemplateId = 1
$filePath = C:\AllSites.txt
$webApplication = Get-SPWebApplication http://webApplicationUrl
$sites = $webApplication.Sites
foreach ($site in $sites)
{
    try
    {
        foreach ($web in $site.AllWebs)
        {
            try
            {
                if ($web.WebTemplateId -eq $webTemplateId) # or use ($web.WebTemplate -eq "STS")
                {
                    Write-Host $web.Url
                    $web.Url | Out-File -FilePath $filePath -Append -Width 256
                }
            }
            finally
            {
                $web.Dispose();
            }
        }
    }
    finally
    {
        if($site -ne $null)
        {
            $site.Dispose();
        }
    }
}

Here are the values of WebTemplateId and the name of out-of-the-box templates in SharePoint 2010. Most of them have the same value for SharePoint 2013 too.

ID Name
0 GLOBAL#0 Global template
1 STS#0 Team Site
1 STS#1 Blank Site
1 STS#2 Document Workspace
2 MPS#0 Basic Meeting Workspace
2 MPS#1 Blank Meeting Workspace
2 MPS#2 Decision Meeting Workspace
2 MPS#3 Social Meeting Workspace
2 MPS#4 Multipage Meeting Workspace
3 CENTRALADMIN#0 Central Admin Site
4 WIKI#0 Wiki Site
9 BLOG#0 Blog
15 SGS#0 Group Work Site
16 TENANTADMIN#0 Tenant Admin Site
2764 ACCSRV#0 Access Services Site
2764 ACCSRV#1 Assets Web Database
2764 ACCSRV#3 Charitable Contributions
2764 ACCSRV#4 Contacts Web Database
2764 ACCSRV#6 Issues Web Database
7 BDR#0 Document Center
14483 OFFILE#0 (obsolete) Records Center
14483 OFFILE#1 Records Center
40 OSRV#0 Shared Services
3100 PPSMASite#0 PerformancePoint
3200 BICenterSite#0 Business Intelligence Center
20 SPS#0 SharePoint Portal Server Site
21 SPSPERS#0 SharePoint Portal Server
22 SPSMSITE#0 Personalization Site
30 SPSTOC#0 Contents area Template
31 SPSTOPIC#0 Topic area template
32 SPSNEWS#0 News Site
39 CMSPUBLISHING#0 Publishing Site
53 BLANKINTERNET#0 Publishing Site
53 BLANKINTERNET#1 Press Releases Site
53 BLANKINTERNET#2 Publishing Site with Workflow
33 SPSNHOME#0 News Site
34 SPSSITES#0 Site Directory
36 SPSCOMMU#0 Community area template
38 SPSREPORTCENTER#0 Report Center
47 SPSPORTAL#0 Collaboration Portal
50 SRCHCEN#0 Enterprise Search Center
51 PROFILES#0 Profiles
52 BLANKINTERNETCONTAINER#0 Publishing Portal
54 SPSMSITEHOST#0 My Site Host
56 ENTERWIKI#0 Enterprise Wiki
90 SRCHCENTERLITE#0 Basic Search Center
90 SRCHCENTERLITE#1 Basic Search Center
2000 SRCHCENTERFAST#0 FAST Search Center
61 visprus#0 Visio Process Repository

String in item’s property bag truncated to 255 characters in a document library

Problem

String value in property bag truncated to 255 characters for some documents in a document library.

Case

There are string values saved in the property bag of documents in a document library. The problem of strings truncated to 255 characters is noticed only in some documents. The problem is tested and is present in SharePoint 2010 and SharePoint 2013. For standard lists (not document libraries) the issue is never noticed.

Investigation

After many tests and investigating the traffic with Fiddler the moment when the trunction happens is discovered. The string is truncated by Office. When a document is modified with Office and saved back in the document library all the values in the property bag of that document are truncated to 255 characters. If many key-value pairs contain strings longer than 255 characters they are all truncated to 255 characters. If checking out the documents for editing is required for the document library then the truncation of the properties does not happen immediately when the changes are saved by the user but when the document is checked in. It is tested with Word, Excel and PowerPoint and the results are the same. The properties are not truncated when the document is viewed or checked in/out without modifying the content of the document.

Solution

The limit of 255 characters is forced by Office and I couldn’t fine (and I think there is no way) to increase this limit. The only solution is to redisign and instead of property in the property bag to use a column of type multi line of text with the property SPFieldMultiLineText.UnlimitedLengthInDocumentLibrary set to TRUE.
Then with a PowerShell script or C# code the values can be copied from the property bag item.Propreties[“YourKey”] to the new field item[“fieldName”].

The already truncated values can be brought back by restoring an older version of the item if the versioning is enabled for the document library.

Conclusion

The item’s property bag should be use to save small pieces of information and is not practical to save strings that may grow big over the time. SharePoint allows saving very long strings in the property bag (tested with string with hundred of milions of characters) but that should not be done especialy in document libraries because of the limits of MS Office.

A better place to save strings longer than 255 characters is a column of type multi line of text with the property SPFieldMultiLineText.UnlimitedLengthInDocumentLibrary set to TRUE. The field can be set to hidden if the users should not be able to view or edit the field value.

More tests (extremes)

While I was at it I continued testing by saving very long strings in the property bag and check how Office reactes on that.

I mentioned above that strings longer than 255 characters in the property bag are not affected by Office when opening the document in read only mode but for extreemlly long strings the Office is affected and it can’t open the document.

After saving string with millions of characters in the property bag Office fails to open the document and one of the following error messages will be shown:

The file Doc1.docx cannot be opened because there are problems with the contents

The file Doc1.docx cannot be opened because there are problems with the contents


Microsoft Word has stopped working

Microsoft Word has stopped working

These error messages are very generic and may be shown for hundreds of different reasons.
The errors are shown randomlly and sometimes Office succeeds to open the document. The random nature of these events can probably be explained with the actual state and available resources to Office on the client machine when it tries to open the problematic documents with extremely long string in the property bag.

LIKE or UNLIKE a list item programmatically

If Likes are enable in the Rating Settings of a list then users can like/unlike a list item using the UI. A list item can also be liked/unliked programmatically.
There are to ways to like/unlike an item programmatically.

1.Using Reputation class.

This is the recommended way.Reputation class is used to set Likes and Ratings for list items.
To like an item:

    Reputation.SetLike(listID, itemID, true);

To unlike an item:

	Reputation.SetLike(listID, itemID, false);

2.Directly by updating the item’s LikesCount and LikedBy fields.

    SPFieldUserValueCollection likedBy = new SPFieldUserValueCollection(web, item["LikedBy"].ToString());

	SPUser user = web.EnsureUser("YOURDOMAIN\\USERNAME");
	SPFieldUserValue newUser = new SPFieldUserValue(web, user.ID, user.Name);
	
	likedBy.Add(newUser);
	int likes = likedBy.Distinct().Count();
	item["LikesCount"] = likes;
	item["LikedBy"] = likedBy;
	item.SystemUpdate(false);

If you increase LikesCount without adding new user in LikedBy field the item will show the increased number of likes temporary but with the first like/unlike from a user using the UI the number of likes will be automatically corrected to reflect the number of users that have liked the item.

To unlike decrease LikeCount field value and remove the user from LikedBy.

“This file cannot be saved because some properties are missing or invalid”

Error


This file cannot be saved because some properties are missing or invalid.
Use the Document Information Panel to provide the correct property values. Errors for required properties are marked with a red asterisk, and errors for invalid properties are marked with a red dashed border.

Only specific pattern allowed. Only data in the following pattern is allowed: ‘.*\S.*’

Case

There is a document library in SharePoint 2010 site. Users are using Office 2010 32-bit.Saving an open document from client computers doesn’t work for all the client PCs. On some PCs saving the document fails and this error is shown: “This file cannot be saved because some properties are missing or invalid. Use the Document Information Panel to provide the correct property values. Errors for required properties are marked with a red asterisk, and errors for invalid properties are marked with a red dashed border”.
In the Document Information Panel there are no properties marked with red and everything looks fine.

Investigation

There are two cases I discovered that experience this problem and both are caused from the same bug in Office.

Case 1:

Labels are enabled for the content type in the document library and the label specified contains line breaks, for example {Field1}\n{Field2}.
The error occurs even without inserting the label in the document’s content. After removing the line breaks from the label the documents can be saved.

Case 2:

There were some hidden columns in the document library’s content type. I made them Optional and opened a document from a PC that is having the problem. In the Document Information Panel there was one property marked with a red dashed border.

This file cannot be saved because some properties are missing or invalid.

Right clicking and selecting “Full error description…” popped-up the following message: “Only specific pattern allowed. Only data in the following pattern is allowed: ‘.*\S.*’”.

“Only specific pattern allowed.

It turns out that the validation is not allowing line breaks in the property. After removing the line breaks I was able to save the document.

The question now is why the problem was present only on some of the PCs. Checking the Office build version discovered that the problem was present only on client PCs with Office 2010 14.0.7106.5003. Client PCs with Office 2010 without service packs (14.0.476.1000) are not having this problem. This is a bug introduced with Office 2010 updates. Microsoft is aware of this bug and they are working on it and will be fixed with the next Office 2010 updates. It’s been few months since then (October 2013) and the issue is still present. Hopefully they will release the hotfix soon.

Solution

(Bug fixed. Check UPDATE 24/03/2014 at the end of this post.)

Because this is a bug in Office 2010, as a temporary “solution” I uninstalled Office 2010 14.0.7106.5003 and installed a clean Office 2010 14.0.476.1000 without service packs. I’ll stick with 14.0.476.1000 until Microsoft fixes this bug. Hopefully it won’t take too long until they release the hotfix.

UPDATE 22/01/2014:
To find out which update is the problem I performed the following:

-Installed clean Office 2010 (32 bit) 14.0.476.100 – The issue is not present

-Then Installed Service Pack 2 14.0.7015.1000 –The issue is not present

-Then installed the updates after SP2 one by one until the problem started.

Discovered that KB2826026 (08/10/2013) is the update that has introduced this bug.

-To make sure that this is the only problematic update I uninstalled KB2826026 and installed the other latest updates (except KB2826026) and the issue is not present.

Microsoft hasn’t released the fix for this bug yet. Uninstalling KB2826026 solves the problem temporarily until the hotfix is released.

UPDATE 25/02/2014:
“The KB2826026 has been replaced by the KB2837583 (released 11/02/2014) but Microsoft did not remove the bug brought by the first KB. So you have to uninstall the KB2837583 otherwise you will have this bug again …” – visitor’s comment (Nicolas).

In my case uninstalling KB2826026 (now KB2837583) fixed the problem. If in your case that doesn’t solve the problem then try by uninstalling other updates released after September 2013. Check the comments below.

UPDATE 12/03/2014:

KB2837583 (released 11/02/2014) is now replaced with KB2878225(release 11/03/2014) but the bug is still not fixed. So this update (KB2878225) must be uninstalled too.

UPDATE 24/03/2014:

Good news! The bug is fixed with hotfix KB2878228. You have to download and install it manually because its not included with the automatic updates.

How to catch ‘Access Denied’ exception with try-catch in SharePoint

Problem:

Can’t catch ‘Access Denied” exception in try-catch block.

Case:

When the code tries to access an object that it has no permissions to access ‘access denied’ exception is thrown and by default it is handled by SharePoint by redirecting to ‘Access Denied’ page. That page doesn’t show much information for troubleshooting.
If you put the code that causes ‘access denied’ exception in try-catch block you will not be able to catch that exception because SharePoint has already handled it.

Solution:

To change this default behavior SPSecurity.CatchAccessDeniedException should be set to FALSE before your try-catch block.

bool originalState = SPSecurity.CatchAccessDeniedException;
SPSecurity.CatchAccessDeniedException = false;
try
{
	//your code that throws Access Denied exception
}
catch
{
	//get more info about the exception 
}
finally
{
	SPSecurity.CatchAccessDeniedException = originalState;
}

Change the URL of a SharePoint list or library

Below are listed three different ways to change the URL of an existing list or library.

1. With PowerShell

Changing the URL is possible by moving the root folder to a new URL. Here is a PowerShell script to change the URL of an existing library:

Add-PSSnapin Microsoft.SharePoint.PowerShell –erroraction SilentlyContinue

$libOriginalUrl = "/Lists/YourLibName1";
$libNewUrl = "/YourLibName2";
$web = Get-SPWeb -Identity http://....

$lib = $web.GetList($web.Url + $libOriginalUrl)
$rootFolder = $lib.RootFolder;
$rootFolder.MoveTo($web.Url + $libNewUrl)

2. With SharePoint Designer

Open the site in SharePoint Designer and in All Files in Site Objects right click the list and click “Rename” to rename it or drag and drop to move it to another folder. For example a list can be moved outside “/Lists” and the Url will change from “webUrl/Lists/List1” to “webUrl/List1”.
All Files in SharePoint Designer

3. With Windows Explorer

Its the same thing as with SharePoint Designer done in Windows Explorer without opening SharePoint Designer. To get to all files and folders of the site open a document library with Windows Explorer by clicking “Open in Explorer” in the Ribbon. Then in Windows Explorer move up in the folders hierarchy to show all the files and folders in the root of the site. You can rename a list/library and you can drag and drop the list’s root folder to add/remove parts of the Url as in the example with SharePoint Designer above. Because standard lists don’t have the option in the Ribbon “Open in Explorer” you can use any document library just to get to the folders of the site as explained in the previous paragraph and then find your list and rename/move it.
Site files in Explorer

Note:

Hyperlinks in Quick Launch will continue to point to the old URL of the list so after changing the URL you need to update the quick launch to reflect the new list name and URL.

Multi lookup fields in CAML queries. <Eq> vs <Contains>.

Correct query with <Eq>:

	<Where><Eq><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">" + id + "</Value></Eq></Where>

Problematic query with <Contains>:

"<Where><Contains><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">" + id + "</Value></Contains></Where>"

Below is a short explanation and comparison between the two queries above.

When using CAML query for querying a list with multi-lookup field it may seem logical to use <CONTAINS> to get all the items that contain a specific value in the multi lookup field.
That query would be:

"<Where><Contains><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">" + id + "</Value></Contains></Where>"

The problem with this query is that it will return wrong results as explained below with one example.
For demonstration purposes let’s say we have two lists, List1 and List2, and List1 has a multi lookup column that points to List2.

These two lists contain the following items:

Items in List1:
  • Item1:
        Title: Item1
        MultiLookupField: i1
  • Item2:
        Title: Item2
        MultiLookupField: i1,i21
  • Item3:
        Title: Item3
        MultiLookupField: i2,i21
    • Items in List2:
      • Item1:
            Title: i1
            ID: 1
      • Item2:
            Title i2
            ID: 2
      • Item3:
            Title: i21
            ID: 21

      A CAML query with <CONTAINS> to get all items in List1 that have i1 in their multi lookup field would look like below:

      	<Where><Contains><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">" + 1 + "</Value></Contains></Where>
      

      This will return Item1, Item2 and Item3. It should only return Item1 and Item2 but because Item3 has i21 which has ID=21 it is included because 21 contains “1”.

      To avoid this problem <EQ> should be used instead of <CONTAINS>.

      	<Where><Eq><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">" + id + "</Value></Eq></Where>
      

      By reading the query you may think that it will return only the items that have ONLY one value in the multi lookup field equal to value specified in the query but the true behaviour is that it will return all the items that CONTAIN the specified item even if they have other items selected in the multi lookup field.
      For the lists in the example above if for example the query is as below

      	<Where><Eq><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">1</Value></Eq></Where>
      

      it will return not just Item1 that has only i1 in the multi lookup field but it will also return Item2 because it contains i1 even although i1 is not the only item Item2 contains in the multi lookup field (it also contains i21).

      Conclusion:

      Use <EQ> instead of <CONTAINS> when writing a query that checks a multi lookup field.
      Be aware that when checking a multi lookup field by Id (with LookupId=TRUE) a query with <EQ> returns results that you would normally expect from a query with <CONTAINS> while a query with <CONTAINS> returns wrong results and is not very usable in this case.

      Code example with <EQ>:

      	string query = "<Where><Eq><FieldRef Name=\"MultiLookupFieldName\" LookupId=\"TRUE\"/><Value Type=\"LookupMulti\">" + id + "</Value></Eq></Where>";
      	SPQuery spQuery = new SPQuery();
      	spQuery.Query = query;
      	SPListItemCollection items = list.GetItems(spQuery);
      


Effects of temporarily disabling versioning for a SharePoint document library

Suppose we have a document library that has major and minor versioning enabled. We want to know what happens if we disable versioning and re-enable it again, and what is lost/retained from the items versions history.

Case:

There is a document library with major and minor versioning enabled, requires check out for editing document and there are workflows associated with the content types. We’ll observe what happens with the version history when the versioning is disabled and re-enabled. When the versioning is disabled the document library contains number of documents in minor and major versions, with version history, some document are in checked out state and there are document with workflows in process.

Test Results

After re-enabled the versioning the version history of the documents is preserved but any change done while the versioning was disabled has increased the major version of the document (document is published).

Example: Before disabling the versioning for the document library one document is in version 0,2 with version history 0.1, 0.2. After the versioning is disabled the item is modified. After re-enabling the versioning the change done while the versioning was disabled has increased the major version of the document (document published), so the versioning history now is 0.1, 0.2, 1.0 instead of 0.1, 0.2, 0.3 how would have been if the versioning was not disabled when the change has happened.

Every document that was not modified while the versioning was disabled has the full version history back and unaffected from the disable-enable versioning process.

Conclusion

If minor versions are enabled for a document library then temporarily disabling versioning will affect the version history if changes are made while the versioning is disabled. The problem is that every change in that period will publish the document to the next major version.

Running code on SharePoint server using application page

When we need to work with SharePoint object model for maintenance reasons or simple tasks we need a way to execute code on a server machine. Example of such situations are: when we want to check the properties of an item, list or web that are not accessible using the UI, or looping through all the items in a list to set a particular field to a specific value etc.
In such cases there are different options to work with SharePoint object model on a server machine where there is no Visual Studio installed:

  1. PowerShell script
  2. Windows application. For example console or WinForms application developed on Development environment by referencing Microsoft.SharePoint.dll
  3. Application page with inline code

This post is a quick how-to guide on using application page to run inline C# code on a server.
This post is not a guide how to develop application pages for a SharePoint solution. In that case you would write the code in the page’s code behind and pack everything in a solution package.

Step 1

Create a file with extension .aspx (in this guide we will name it appPage1.aspx)

Step 2

Copy and paste the following content to appPage1.aspx based on the SharePoint version you need the page for.

For Microsoft Office SharePoint Server 2007 (MOSS 2007):

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C" %>
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.Utilities" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormSection" Src="~/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" Src="~/_controltemplates/InputFormControl.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" Src="~/_controltemplates/ButtonSection.ascx" %>
<%@ Register TagPrefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
        <%
            try
            {
                SPWeb web = SPContext.Current.Web;
		        Response.Write(web.Title);
                //your code here
            }                
            catch (Exception ex)
            {
                Response.Write("<br/><br/>" + ex.ToString());
            }

        %>
</asp:Content>

<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
</asp:Content>

For SharePoint 2010:

<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" DynamicMasterPageFile="~masterurl/default.master" Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
        <%
            try
            {
                SPWeb web = SPContext.Current.Web;
		        Response.Write(web.Title);
                //your code here
            }                
            catch (Exception ex)
            {
                Response.Write("<br/><br/>" + ex.ToString());
            }

        %>
</asp:Content>

<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
</asp:Content>

For SharePoint 2013

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%> 
<%@ Page Language="C#" MasterPageFile="minimal.master"  %> 
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> 
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Import Namespace="Microsoft.SharePoint" %> 
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="wssuc" TagName="LinkSection" src="~/_controltemplates/15/LinkSection.ascx" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
        <%
            try
           {
                SPWeb web = SPContext.Current.Web;
		        Response.Write(web.Title);
                //your code here
            }              
            catch (Exception ex)
            {
                Response.Write("<br/><br/>" + ex.ToString());
            }

        %>
</asp:Content>

<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
</asp:Content>

Step 3

Write your code in the space specified with “//your code here” in the aspx page.
If you need you can reference your assemblies in the page. The referenced assemblies must be available on the server where this page will be used.

Step 4

Copy the file to the LAYOUTS folder in SharePoint root directory.
The path to the layouts folder is:

For MOSS 2007:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\Template\Layouts

For SharePoint 2010

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Template\Layouts

For SharePoint 2013

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\Template\Layouts

Step 5

Open the browser and navigate to appPage1.aspx.
The Url is:

For MOSS 2007 and SharePoint 2010:

yourSiteUrl/_layouts/appPage1.aspx

For SharePoint 2013:

yourSiteUrl/_layouts/15/appPage1.aspx

The code will run in the context of the web where you open the page and in the context of the logged in user. You can elevate the privilages of the logged in user with SPSecurity.RunWithElevatedPrivilages and instantiate new SPSite to run the code in the context of system account.

Modifying a sealed choice field using PowerShell

To modify a sealed field it must be first “unsealed” by setting Sealed property to False. This can be done using PowerShell.

For demonstration purposes let’s say we have a list List1 which contains a sealed choice field ChoiceField1 with three choices Choice #1, Choice #2, Choice #3, and the default choice is Choice #1.

Unseal the field

Below is the script to first “unsealed” the field

	Add-PSSnapin Microsoft.SharePoint.PowerShell –erroraction SilentlyContinue
	$web = Get-SPWeb -Identity "http://weburl"
	$list = $web.GetList($web.Url + "/Lists/List1")
	$field = $list.Fields.GetField("ChoiceField1")
	$field.Sealed = $false
	$field.Update()

Change the choices order

To change the choices order from Choice #1, Choice #2, Choice #3 to Choice #2, Choice #3, Choice #1 run the script below

	$web = Get-SPWeb -Identity "http://weburl"
	$list = $web.GetList($web.Url + "/Lists/List1")
	$field = $list.Fields.GetField("ChoiceField1")
	$field.Choices.Remove("Choice #1");
	$field.Choices.Remove("Choice #2");
	$field.Choices.Remove("Choice #3");
	$field.Choices.Add("Choice #2")
	$field.Choices.Add("Choice #3")
	$field.Choices.Add("Choice #1")
	$field.Update()

Change the default choice

To change the default choice from Choice #1 to Choice #3 run the script below

	$web = Get-SPWeb -Identity "http://weburl"
	$list = $web.GetList($web.Url + "/Lists/List1")
	$field = $list.Fields.GetField("ChoiceField1")
	$field.DefaultValue = "Choice #3"
	$field.Update()

Seal the field back

	$web = Get-SPWeb -Identity "http://weburl"
	$list = $web.GetList($web.Url + "/Lists/List1")
	$field = $list.Fields.GetField("ChoiceField1")
	$field.Sealed = $true
	$field.update()
	$web.Dispose()

For every script above I used new $web so every script is ready for use. You can get one $web and combine pieces of the above scripts.

Content Approval implementation options in SharePoint

There are different options to implement item approval functionality for SharePoint list items. Below are listed some option starting from the simplest, out-of-the-box features to workflows developed in Visual Studio.

1. Set ‘Require content approval’ in list’s Versioning settings.

This is the simplest way to have content approval functionality in SharePoint and it doesn’t require workflow.
To enable Content Approval go to list’s Settings -> Versioning settings and set ‘Require content approval for submitted items?’ to Yes.
Enable versioning as per your requirements.
Workflow Settings

2. Approval Workflow.

This is achieved by using out-of-the-box workflow ‘Approval – SharePoint 2010’ workflow and involves task generation and options to Approve/Reject/Request changes/Reassign tasks)
To use this workflow follow the steps below:
1. Make sure the site collection feature ‘Workflows’ is activated
2. Go to List Settings -> Workflow Settings and click ‘Add’ to add new workflow. Choose Approval – SharePoint 2010, specify Name, choose Task List, History List and specify the Start Options.
Require Content Approval screen
Click Next and specify the other settings (Approvers, task duration, enable content approval etc.)
After adding the workflow it will be available for the list items and it will start automatically or can be started manually depending on the workflow settings.

3. Create custom workflow using SharePoint Designer

With SharePoint Designer can be created custom workflows with a relatively easy way and without coding. SharePoint Designer has improved noticeably with every major version and there is a lot of flexibility and has a quite long list of actions to use when creating workflows for different business processes including content approval processes.

4. Creating workflows using Visual Studio

This is the most advanced way of creating custom workflows. It has full flexibility to implement any functionality and has the advantage of building workflows for easy deployment in different environments.

Conclusion

There is no strict rule when to use which option. Its best to evaluate every option starting from the easiest one (option 1) until the most appropriate option is chosen considering the requirements and the available resources (time and people)

%d bloggers like this: