2014-10-29

Visual Studio 2013 Cannot Deploy Content Type Updates

After spending 10+ hours trying to figure out why content types included in my wsp cannot be deployed using Visual Studio 2013 deploy without recreating SP site I must say I am disappointed with my findings. Content types cannot be removed from SP site by using Visual Studio 2013 Retract command. My colleague can successfully deploy (and retract) content types using Visual Studio 2012, so I decided to look inside what is really happening under the hood.

Apparently, both Visual Studio versions, 2012 and 2013, are using Microsoft.VisualStudio.SharePoint.Commands.Implementation.V5.dll to deploy and retract SharePoint solutions to SharePoint sites. But the big difference between the two is that VS 2012 implements Microsoft.VisualStudio.SharePoint.Commands.DeploymentManager.DeactivateFeatures in a way so that it calls SPFeatureCollection.Remove method with force argument set to true which causes a call to stored procedure proc_DeactivateContentTypeInScope with SQL parameter @IsDeactivatingFeature set to 2. When this parameter is set to 2 then stored procedure skips a call to another stored procedure named proc_IsContentTypeInUse. This basically forces the deletion of content type.

VS 2013 implements Microsoft.VisualStudio.SharePoint.Commands.DeploymentManager.DeactivateFeatures with a call to Microsoft.VisualStudio.SharePoint.Commands.DeploymentManager.DeactivateFeature method which makes a call to SPFeatureCollection.Remove with force argument set to false which causes a call to stored procedure proc_DeactivateContentTypeInScope with SQL parameter @IsDeactivatingFeature set to 1. This prevents content type from being deleted.

I am using version of VS 2013 which is Visual Studio Premium 2013 Version 12.0.30723.00 Update 3.

I hope MS provides a fix to this issue soon. Until then I am reverting to VS 2012.

2014-09-22

ECB Missing on Manage User Profiles page in Central Administration

Today I was working on SharePoint 2013 dev machine when I was trying to sync AD accounts with SharePoint. I wanted to set emails for accounts to test workflow tasks but I was surprised I couldn't edit user profiles because ECB menus were not rendered on Manage User Profiles (ProfMngr.aspx) page:

Apparently the problem lies in the fact that CA site was browsed with IE 11. To fix this problem, site has to be added to compatibility view list. In Internet Explorer 11 open Tools menu and choose Compatibility View Settings menu item while browsing CA site. The following window should open:

Click the Add button. Window should now look like this:

Close the window and voila, now ECB should be rendered. Notice how grid lines are added to the list:


2014-08-26

Error occurred in deployment step ‘Activate Features': System.TimeoutException: The HTTP request has timed out after 20000 milliseconds.

I am running SP 2013 dev environment and I recently ran into a problem described in the post title. I tried to deploy a solution with feature activation which included SP 2013 Workflow SPIs. I tried to solve the issue by applying a solution described here but it wasn't helpful in my situation. I already had registry settings set up to extend timeout period for SharePoint deployment.

Visual studio output window might look like this:
  Activating feature 'Feature1' ...
Error occurred in deployment step 'Activate Features': System.TimeoutException: The HTTP request has timed out after 20000 milliseconds. ---> System.Net.WebException: The request was aborted: The request was canceled.
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at Microsoft.Workflow.Client.HttpGetResponseAsyncResult`1.OnGotResponse(IAsyncResult result)
   --- End of inner exception stack trace ---
   at Microsoft.Workflow.Common.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at Microsoft.Workflow.Client.Ht


I noticed that I had Fiddler2 running in the background while trying to deploy my solution. I exited Fiddler2 application and tried to deploy again. This time deploy was successful. It very well might be a cause of error. Just to be sure I tried to reproduce the same behavior again by starting Fiddler2 again and trying to deploy the wsp using Visual Studio 2012. This time VS couldn't delete workflows and workflow associations from the site but it was able to deploy successfully. I turned off Fiddler2 again and tried to deploy, and it went smooth, without any errors. I might be wrong but it seems to me that Fiddler's proxy is interfering with deployment of workflows.

I know there are many reasons causing this error but this solution helped me. Hopefully it helps someone else.

2014-08-20

Implementation of ribbon button availability by calling asynchronous methods

It is known that SharePoint ribbon can be extended by implementing CustomAction Ribbon.Library.Actions.AddAButton. SharePoint allows you to dynamically control whether this ribbon button should be enabled. This is done by implementing Javascript logic in CommandUIHandler's EnabledScript attribute.

The problem is if you want to include asynchronous call in aforementioned attribute because this Javascript block must return boolean value. So, when SharePoint calls asynchronous method it will not get the result immediately. Instead, it will get the result of async call in callback function but it's too late, Javascript block has already returned.

The solution to this problem was very well described by Andrew Connell on his blog post. Since no code snippets were included in this post I decided to provide it here because there is one caveat that is not so obvious from first look at the Andrew's post.

This is the implementation of ribbon button in Elements.xml:

    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition
          Location="Ribbon.Library.Share.Controls._children">
          <Button Id="Ribbon.Library.Share.NewRibbonButton"
                  Command="NewRibbonButtonCommand"
                  Image16by16="/_layouts/15/Style/buttonIcon16.png"
                  Image32by32="/_layouts/15/Style/buttonIcon32.png"
                  LabelText="New case"
                  ToolTipTitle="Get new case."
                  ToolTipDescription="Assigns new case to you."
                  TemplateAlias="o2" />
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler
          Command="NewRibbonButtonCommand"
          EnabledScript="javascript:IsCurrentUserMemberOfGroup('puk ref');       
       "/>
      </CommandUIHandlers>
    </CommandUIExtension>

Since our project doesn't include customized forms nor master pages we had to include external Javascript file to Elements.xml:

  <CustomAction
   ScriptSrc="/_layouts/15/Scripts/SPRibbonHelperScript.js"
   Location="ScriptLink"
   Sequence="1001">
  </CustomAction>

This makes it easier to debug the solution if Javascript code is included in external file. Prerequisite for this step is to add new Javascript file to layouts mapped folder.

Next piece of code is the actual implementation of enabling/disabling the button based on current user's group membership in SPRibbonHelperScript.js:

var isMemberOfGroup = false;

function IsCurrentUserMemberOfGroup(groupName) {
    var currentContext = new SP.ClientContext.get_current();
    var currentWeb = currentContext.get_web();
    var currentUser = currentContext.get_web().get_currentUser();
    currentContext.load(currentUser);

    var allGroups = currentWeb.get_siteGroups();
    currentContext.load(allGroups);

    var group = allGroups.getByName(groupName);
    currentContext.load(group);

    var groupUsers = group.get_users();
    currentContext.load(groupUsers);
    currentContext.executeQueryAsync(OnSuccess, OnFailure);

    function OnSuccess(sender, args) {
        var userInGroup = false;
        var groupUserEnumerator = groupUsers.getEnumerator();
        while (groupUserEnumerator.moveNext()) {
            var groupUser = groupUserEnumerator.get_current();
            if (groupUser.get_id() == currentUser.get_id()) {
                userInGroup = true;
                break;
            }
        }

        if (isMemberOfGroup == false || isMemberOfGroup == 'undefined') {
            if (userInGroup == true) {
                isMemberOfGroup = userInGroup;
                RefreshCommandUI();
            }
        }
    }

    function OnFailure(sender, args) {
    }

    return isMemberOfGroup;
}

2013-12-09

SPListItem with too many SPListItemVersions

Couple days ago I had a problem on a production WFE SharePoint 2010 server which manifested itself in massive slowdown. Since this is public facing internet site we needed to react fast. By filtering the Application Event Log I quickly found a reason for slowdown. Apparently, server was running out of memory. Since production farm consists of 5 servers which are very well equiped with adequate amount of RAM, there was only a little chance that poor HW configuration caused the slowdown.


This is how entry in Event Log looked like:

Exception: System.OutOfMemoryException

StackTrace:    at Microsoft.SharePoint.Library.SPRequestInternalClass.GetListItemDataWithCallback(String bstrUrl, String bstrListName, String bstrViewName, String bstrViewXml, SAFEARRAYFLAGS fSafeArrayFlags, ISP2DSafeArrayWriter pSACallback, ISPDataCallback pPagingCallback, ISPDataCallback pSchemaCallback, Boolean& pbMaximalView)
   at Microsoft.SharePoint.Library.SPRequest.GetListItemDataWithCallback(String bstrUrl, String bstrListName, String bstrViewName, String bstrViewXml, SAFEARRAYFLAGS fSafeArrayFlags, ISP2DSafeArrayWriter pSACallback, ISPDataCallback pPagingCallback, ISPDataCallback pSchemaCallback, Boolean& pbMaximalView)
   at Microsoft.SharePoint.SPListItemVersionCollection.EnsureVersionsData()
   at Microsoft.SharePoint.SPListItemVersionCollection.get_Count()
   at In2.Vuk.Cl.SharePointHelpers.VukSPListItemHelper.GetLastPublishedVersion(SPListItem listItem)
   at In2.Vuk.Cl.Propisi.SpRepositories.SviPropisiRepository.GetParentsId(String idNodea, Boolean forAdministracijaPropisa)
   at In2.Vuk.Cl.Propisi.SpRepositories.SviPropisiRepository.GetLastVersionFromNode(String idNodea)
   at PrikazPropisaService.GetParentsId(String tipNodea, String idNodea, Boolean povj, Int32 ver)
   at SyncInvokeGetParentsId(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
   at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
   at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
   at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
   at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
   at System.ServiceModel.Channels.InputQueueChannel`1.EnqueueAndDispatch(TDisposable item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
   at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
   at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, ItemDequeuedCallback dequeuedCallback)
   at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
   at System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
   at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
   at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
   at System.ServiceModel.PartialTrustHelpers.PartialTrustInvoke(ContextCallback callback, Object state)
   at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequestWithFlow(Object state)
   at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
   at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
   at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
   at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(Object state)
   at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)



I immediatelly traced the error to my custom webpart, specifically to the method GetLastPublishedVersion:
     
 public static SPListItemVersion GetLastPublishedVersion(SPListItem listItem)
        {
            for (int i = 0; i < listItem.Versions.Count; i++)
            {
                if ((bool)listItem.Versions[i][Constants.Published])
                {
                    return listItem.Versions[i];
                }
            }
            return null;
        }


I figured from the stack trace that the first line in SP Server Object Model which was called was: 
Microsoft.SharePoint.SPListItemVersionCollection.get_Count()

This corresponds to listItem.Versions.Count property call within for loop condition. Next method in Server Object Model which is being called is EnsureVersionsData which is actually fetching entire SPListItemVersion collection for the SPListItem parameter of method in my webpart. When reflecting SPListItemVersionCollection class I found out there was no way to fetch a particular version of SPListItem, not even with methods GetVersionFromID nor GetVersionFromLabel!!! Since all versions of problematic SPListItem were consuming more than 2GB of memory there was no way to delete unnecesary versions because there was no way to fetch any of the versions.

I ran out of ideas, so I called my colleague who is an expert in crisis situations. When examining EnsureVersionsData method implementation using .NET Reflector he realized that there was a piece of code inside Microsoft.Sharepoint.dll which was testing private field variable named m_scFields. This field is used by SOM to set all the columns of a custom SPList that are returned from content database.

The rest was easy, by using .NET reflection we needed to set this field to limit SPListItemVersionCollection to return only columns which are not holding large data. A few other columns were added to m_scFields field in order to successfully call Delete method.


In DeleteUnneededVersions method I  test Published custom column which is specific to my custom list. If SPListItemVersion has this column set to false then it is not needed by my webpart therefore it can be safely deleted. By runing this code on every SPListItemversions have shrunk to an acceptable count. Complete method implementation is as follows:
       
private static void DeleteUnneededVersions(SPListItem item2)
        {
            SPListItemVersionCollection col = item2.Versions;
            StringCollection collection = new StringCollection();
            collection.Add("Title");
            collection.Add("Published");
            collection.Add("owshiddenversion");
            collection.Add("_UIVersion");
            collection.Add("_IsCurrentVersion");
            typeof(SPListItemVersionCollection)
               .GetField("m_scFields", BindingFlags.Instance | BindingFlags.NonPublic)
               .SetValue(col, collection);

            bool publishedReached = false;
            for (int i = 0; i < item2.Versions.Count; i++)
            {
                SPListItemVersion ver = item2.Versions[i];
                bool published = (bool)ver["Published"];
                if (published)
                {
                    publishedReached = true;
                }

                if (!published && publishedReached)
                {
                    ver.Delete();
                    i--;
                }
            }
        }