SharePoint Developer Support Team Blog

The Official blog of the SharePoint Developer Support Team

June, 2013

  • HOW TO: Access list item in WorkflowStarting event receiver when “Required documents to be checked out” option is set to “Yes”

    This post is a contribution from Charls Tom Jacob, an engineer with the SharePoint Developer Support team.

    Scenario:

    You have a SharePoint library in which “Require documents to be checked out before they can be edited?” is set to Yes. You have a WorkflowStarting event receiver associated to the list, with the following very basic implementation:

    public override void WorkflowStarting(SPWorkflowEventProperties properties)
    {
        SPList list = properties.ActivationProperties.List;
        int id = properties.ActivationProperties.ItemId;
        SPListItem currentitem = list.GetItemById(id);
    }

    Issue:

    When the event receiver is triggered, the above code will fail with the following exception while getting the item:

    ArgumentException was unhandled by user code: Item does not exist. It may have been deleted by another user.
     
    at Microsoft.SharePoint.SPList.GetItemById(String strId, Int32 id, String strRootFolder, Boolean cacheRowsetAndId, String strViewFields, Boolean bDatesInUtc)
    at Microsoft.SharePoint.SPList.GetItemById(Int32 id)
    at WorkflowEventTest.EventReceiver1.EventReceiver1.WorkflowStarting(SPWorkflowEventProperties properties)
    at Microsoft.SharePoint.Workflow.SPWorkflowEventManager.RunWorkflowEventReceiverHelper(Object receiver, SPUserCodeInfo userCodeInfo, Object properties, SPEventContext context, String receiverData)
    at Microsoft.SharePoint.SPEventManager.<>c__DisplayClassc`1.<InvokeEventReceivers>b__6()
    at Microsoft.SharePoint.SPSecurity.RunAsUser(SPUserToken userToken, Boolean bResetContext, WaitCallback code, Object param)

    Reason:

    When “Require documents to be checked out before they can be edited?” is set to Yes, the uploaded item gets exclusively checked out to the user who uploaded it. This means item won’t be viewable/accessible to ANY other user. This is because the workflow starting event is triggered way before the upload process is fully complete. When “Require documents to be checked out ” is Yes, user is presented with an additional page which says “The document was uploaded successfully and is checked out to you. Check that the fields below are correct and that all required fields are filled out. The file will not be accessible to other users until you check in.”

    image

    Event receiver executes while the user is presented with this popup screen, and it executes in the context of SHAREPOINT\SYSTEM account and runs into this error.

    NOTE: Exclusive checkout applies to another scenario, where the user uploads the document but cancels the check-in popup; The document remains checked out and accessible only to the same user.

    Solution: Impersonate the user account while accessing the list item.

    public override void WorkflowStarting(SPWorkflowEventProperties properties)
    {
        base.WorkflowStarting(properties);
        SPUserToken token = properties.ActivationProperties.Web.AllUsers[properties.ActivationProperties.Originator].UserToken;
        SPSite site = new SPSite(properties.ActivationProperties.Web.Url, token);
        SPWeb web = site.OpenWeb();
        SPList list = web.Lists[properties.ActivationProperties.ListId];
        SPListItem item = list.GetItemById(properties.ActivationProperties.ItemId);
    }

    With the above code, item is accessed using the same user account which was used to upload the document, hence the code succeeds in getting the item.

    Hope this information was helpful!

  • HOW TO: Allow anonymous users to add items to SharePoint list using client object model

    This post is a contribution from Charls Tom Jacob, an engineer with the SharePoint Developer Support team.

    This blog is based on a support ticket I handled recently. The requirement was to let anonymous users add items to a SharePoint list/library using client side object model(JavaScript).

    Here are the steps needed to implement this.

    1. Enable Anonymous access at the Web Application level

    Go to SharePoint Central Administration => Application Management, select the Web Application => Authentication Providers, click on the zone, and select Enable anonymous access

    clip_image002

    2. Now, navigate to the corresponding web site, Site Action => Site Permissions => Anonymous Access.

    Configure what anonymous users can access at the site level

    clip_image004

    3. Break the permission inheritance on the specific list in which you want to allow anonymous users to add items.

    Navigate to the list/library settings for the list in which you want to allow Anonymous users to add items:

    List Settings => Permissions for this list => Stop Inheriting Permissions, click OK => Anonymous Access

    Select Add Items.

    clip_image006

    4. Enable “AddItem” operation for the client object model for the Web Application. You can run the following code as a console application (equivalent powershell script also should work).

    // Allows AddItem operation using anonymous access

    private static voidAllowAnonAccess(){

                Console.WriteLine("Enabling Anonymous access....");

                SPWebApplication webApp = SPWebApplication.Lookup(new Uri(webAppUrl));

                webApp.ClientCallableSettings.AnonymousRestrictedTypes.Remove(typeof(Microsoft.SharePoint.SPList), "GetItems");

                webApp.ClientCallableSettings.AnonymousRestrictedTypes.Remove(typeof(Microsoft.SharePoint.SPList), "AddItem");

                webApp.Update();

                Console.WriteLine("Enabled Anonymous access!");  

            } 

    // Revokes Add/Get Item operation using anonymous access

            private static voidRemoveAnonAccess(){

                Console.WriteLine("Disabling Anonymous access....");

                SPWebApplication webApp = SPWebApplication.Lookup(new Uri(webAppUrl));

                webApp.ClientCallableSettings.AnonymousRestrictedTypes.Add(typeof(Microsoft.SharePoint.SPList), "GetItems");

                webApp.ClientCallableSettings.AnonymousRestrictedTypes.Add(typeof(Microsoft.SharePoint.SPList), "AddItem");

                webApp.Update();

                Console.WriteLine("Disabled Anonymous access!"); 

            }

    Please do IISRESET after running this code

    You can make use of the createListItem() and retrieveListItems() method discussed in the following articles to Add/Get items using the anonymous account:

    How to: Retrieve List Items Using JavaScript

    http://msdn.microsoft.com/en-us/library/hh185007(v=office.14).aspx

    Creating a List Item Using ECMAScript (JavaScript, JScript)

    http://msdn.microsoft.com/en-us/library/hh185011(v=office.14).aspx

    Note: If you are using a custom layouts page to execute the JS Script, make sure it’s accessible to the anonymous users. Inherit the custom page from UnsecuredLayoutsPageBase with AllowAnonymousAccess set to true.

    Anonymous layouts page:

    clip_image001

    Item created by the anonymous user:

    clip_image002

     

    Sample code for this is available for download.

    Happy anonymous access!!

  • Could not establish trust relationship for SSL/TLS secure channel

    This post is a contribution from Amy Luu, an engineer with the SharePoint Developer Support team.

    You can get the following error when SharePoint communicates to an external service via HTTPS either within the same server or a different server.

    Could not establish trust relationship for the SSL/TLS secure channel, or
    Remote certificate is invalid according to the validation procedure, or
    An operation failed because the following certificate has validation errors

    Typically, the scenario will be that a custom component that resides in SharePoint 2010/2013 calls WCF service over HTTPS on the same or different server.  The reason for this is that SharePoint implements its own certificate validation policy to override .NET certificate validation.

    Fix is to setup a trust between SharePoint and the server requiring certificate validation.

    In SharePoint Central Administration site, go to “Security” and then “Manage Trust”.  Upload the certificates to SharePoint.  The key is to get both the root and subordinate certificates on to SharePoint.

    The steps to get the certificates from the remote server hosting the WCF service are as follows:

    1.  Browse from IE to the WCF service (e.g., https://remotehost/service.svc?wsdl)

    2.  Right click on the browser body and choose “Properties” and then “Certificates” and then “Certificate Path”.

    This tells you the certificate chain that’s required by the other server in order to communicate with it properly.  You can double-click on each level in the certificate chain to go to that particular certificate, then click on “Details” tab, “Copy to File” to save the certificate with the default settings.

    As an example, get both VeriSign & VeriSign Class 3 Extended Validation SSL CA.

    image

    Hope this helps!

  • Crawled properties won’t re-appear after deletion for a custom XML indexing connector in SharePoint 2013

    A customer reported this problem to us and it was quite a learning to know how content processing works in SharePoint 2013 with respect to crawled properties from a custom XML indexing connector.  Thought this could be worth sharing!

    So, here’s the problem.  Follow the steps outlined in this blog post Create a custom XML indexing connector for SharePoint 2013 and perform a full crawl of the custom XML connector content source.  Go to Search Administration > Search Schema > Categories.  Look at newly created custom XML connector category, you should see 18 crawled properties in there.

    image

    Use the ECB menu against the custom XML connector category called “XML Connector” and click “Edit Category”.  Click “Delete all unmapped crawled properties” button to delete all the crawled properties available through the custom XML connector (screenshot below).  Since we have not associated any crawled property to a managed property, all crawled property will be deleted.

    image

    Perform a full/incremental crawl of the content source again.  Once done, check the crawled properties available for the custom XML connector category.  There will be none available Sad smile  That’s the problem!!!

    On investigation and collaborating with the search development team, it was learnt that this behavior is by design.  Here’s the technical details.

    CTS (Content Transformation Service) maintains an in-memory cache of crawled properties.  And on subsequent crawls, it skips the properties that’s already in the cache.  This design is for performance reasons.  Since extracting crawled properties and updating them in schema is an intensive operation, CTS is designed this way so that only new crawled properties are crawled on subsequent attempts thus resulting in a much faster throughput in terms of crawl speed.  This design also helps in reducing roundtrips to multiple databases as the property reporter talks to both schema object model and search admin database.

    So here’s how to get back the crawled properties.  There are two ways and these can be used as the scenario may be at your environments/deployments.

    1. Restart search host controller services.

    Open SharePoint 2013 Management Shell in admin mode and issue the command:

    Restart-Service spsearchhostcontroller

    This will restart all of the search services.

    2. The above might not be ideal on environments where all search services are running on single server since it will cause a downtime for all search services (all noderunner.exe will be killed and spawned).  In this case, the specific content processing process can be killed (with a command as shown below) and it will be spawned again immediately.

    PS C:\> Get-WmiObject Win32_Process -Filter "Name like '%noderunner%' and CommandLine like '%ContentProcessing%'" | %{ Stop-Process $_.ProcessId }

     

    Hope this post was informative and helpful.

    Cross post from http://blogs.msdn.com/sridhara
  • Enabling ScriptManager PageMethods doesn’t work in some scenarios in SharePoint 2013

    I worked on a case where the problem was that PageMethods won’t execute from within a SharePoint 2013 application page (page deployed to the _layouts folder).  To replicate the problem, I had a custom master page deployed through a VS 2012 solution with the following definition within the <body/> element.

    <asp:ScriptManager id="ScriptManager" runat="server" EnablePageMethods="true" EnablePartialRendering="true" EnableScriptGlobalization="false" EnableScriptLocalization="true" />
     
          <script type="text/javascript">
     
              function CallMe() {
                  alert("called from master page");
                  // call server side method
                  PageMethods.TestMethod(OnRequestComplete, OnRequestError);
              }
     
              
              function OnRequestComplete(res, userContext, methodName) {
     
                  alert(res);
     
              }
     
              function OnRequestError(error, userContext, methodName) {
     
                  if (error != null) {
     
                      alert(error.get_message());
     
                  }
     
              }
     
        </script>

    And I had my TestMethod defined in my application page as shown below.

    using System;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using System.Web.Services;
     
    namespace SPProj.Layouts.SPProj
    {
        public partial class ApplicationPage1 : LayoutsPageBase
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                Button1.Attributes.Add("onclick", "javascript:CallMe()");
            }
     
            [WebMethod]
            public static string TestMethod()
            {
                return "Hello";
            }
        }
    }

    Notice the ‘onclick’ attribute added to the server-side button control in the Page_Load event.  The idea is, when this page is loaded and the button is clicked, we call the “CallMe()” function defined in the master page.  And this client-side function will call the server-side TestMethod method by virtue of having PageMethods enabled.  But this is what happens in SharePoint 2013.

    The page loads.

    image

    Click the button.

    image

    Click OK on this alert and the second message box won’t come up.  You might have already noticed the JavaScript error in IE status bar.  Double-click on it and you’ll see this error.

    image

    Apparently, this is because of anonymous JavaScript functions being pushed to the page by the new web-scoped feature called “Following Content”.  Here’s a snippet from IE Developer Tools.  For brevity, only relevant snippet is shown.

    PageMethods.set_path("applicationpage1.aspx");
    PageMethods.TestMethod= function(onSuccess,onFailed,userContext) {
    /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
    /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
    /// <param name="userContext" optional="true" mayBeNull="true"></param>
    PageMethods._staticInstance.TestMethod(onSuccess,onFailed,userContext); }
     
    // from custom action with id = "FollowingCalloutScriptBlock"
    (function(){
     
            if (typeof(_spBodyOnLoadFunctions) === 'undefined' || _spBodyOnLoadFunctions === null) {
                return;
            }
            _spBodyOnLoadFunctions.push(function() {

    The problem is that when PageMethods are pushed to the page, a semi-colon isn’t added to properly terminate the JavaScript block.  This is actually not a SharePoint but ASP.NET issue that PageMethods are not properly block terminated.  Since the following JavaScript code is anonymous function at runtime it’s considered to be in the same block and executed, which causes the object expected error.

    There are multiple ways to fix it.

    1. Deactivate “Following Content” feature.  Not a good one, but if this feature is not being used (which is highly unlikely), then simply deactivating it resolves this error.

    2. Modify the “Following Content” feature.  Change the 2 functions defined in the ScriptBlock of <CustomAction/> element to be named functions instead of anonymous.  Deactivate/Uninstall “Following Content” feature.  Install/Activate “Following Content” feature.  THIS OF COURSE, IS UNSUPPORTED.

    3. Override the Render method in the application page and inject a semi-colon character.  Below is the modified application page code.

    using System;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using System.Web.Services;
     
    namespace SPProj.Layouts.SPProj
    {
        public partial class ApplicationPage1 : LayoutsPageBase
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                Button1.Attributes.Add("onclick", "javascript:CallMe()");
            }
     
            [WebMethod]
            public static string TestMethod()
            {
                return "Hello";
            }
     
            protected override void Render(System.Web.UI.HtmlTextWriter writer)
            {
                System.Web.UI.ScriptManager.RegisterClientScriptBlock(this, typeof(ApplicationPage1), "semicolon", ";", true);
                base.Render(writer);
            }
        }
    }

    Once you deploy this solution, browse to the application page and watch the scripts pushed using IE Developer Tools, you’ll see this.

    PageMethods.set_path("applicationpage1.aspx");
    PageMethods.TestMethod= function(onSuccess,onFailed,userContext) {
    /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
    /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
    /// <param name="userContext" optional="true" mayBeNull="true"></param>
    PageMethods._staticInstance.TestMethod(onSuccess,onFailed,userContext); }
    ;
    // from custom action with id = "FollowingCalloutScriptBlock"
    (function(){
     
            if (typeof(_spBodyOnLoadFunctions) === 'undefined' || _spBodyOnLoadFunctions === null) {
                return;
            }
            _spBodyOnLoadFunctions.push(function() {

    Notice that “;” character after the PageMethods declaration just before the anonymous functions from “Following Content” feature starts.  Now, when the application page is browsed.

    image

    And the button is clicked.

    image

    The first alert is displayed and when you click OK, you’ll see the second alert as well.

    image

    That resolves this issue.  Hope this post was helpful.

    Cross post from http://blogs.msdn.com/sridhara