Wednesday, July 22, 2015

Version Conflict while updating list items in SharePoint using ECMAScript/JSOM/CSOM

I thought I'd post this for my own records so I have somewhere to refer back to it. I've also posted it because there was very little help regarding the problem on the Internet and the workarounds proposed weren't that nice. Many ranged from hacks that involved forcing the page to reload itself or having to use CAML instead.
Anyway here is the scenario, the text book piece of code below is used to update a list item in SharePoint using JavaScript. Everything works fine, however if someone else updates a record on another machine after you and then you update the same record on your machine you'll get the "Version Conflict" error.

   1:  function updateListItem(id, statusField, valueToChangeTo, listName, newparentId, parentField) {
   2:            
   3:            var ctx = SP.ClientContext.get_current();
   4:   
   5:            var list = ctx
   6:                      .get_web()
   7:                      .get_lists()
   8:                      .getByTitle(listName);
   9:   
  10:              var item = list.getItemById(id);
  11:                 
  12:             item.refreshLoad();
  13:           
  14:             item.set_item(statusField, valueToChangeTo);
  15:             item.set_item(parentField, newparentId);
  16:   
  17:             item.update();
  18:            
  19:            ctx.executeQueryAsync(function () {
  20:                console.log("New value: ", item.get_item(statusField));
  21:            })
  22:        };

So what went wrong? Well basically the object that you're accessing is a cached object, you retrieved on the first time you saved the item. Since someone else changed the object before you this time your cached object is going to cause a version conflict as SharePoint as a newer version of the item.
How do I solve the problem? You need to load the object again and then update it.
   1:  function updateListItem(id, statusField, valueToChangeTo, listName, newparentId, parentField) {
   2:   
   3:            var ctx = SP.ClientContext.get_current();
   4:   
   5:            var list = ctx
   6:                      .get_web()
   7:                      .get_lists()
   8:                      .getByTitle(listName);
   9:   
  10:            var item = list.getItemById(id);
  11:   
  12:            ctx.load(item)
  13:   
  14:            ctx.executeQueryAsync(function () {
  15:   
  16:                updateListitemAfterData(item, statusField, valueToChangeTo, parentField, newparentId);
  17:            })
  18:        }
  19:   
  20:        function updateListitemAfterData(item, statusField, valueToChangeTo, parentField, newparentId) {
  21:            var ctx = SP.ClientContext.get_current();
  22:            item.set_item(statusField, valueToChangeTo);
  23:            item.set_item(parentField, newparentId);
  24:            item.update();
  25:   
  26:            ctx.executeQueryAsync(function () {
  27:   
  28:                console.log("New value: ", item.get_item(statusField));
  29:            })
  30:   
  31:   
  32:        }
So in the code above I call updateListItem with my values. This then goes and loads the list item fresh from SharePoint and waits using an async call. Once it gets this async call it callsupdateListItemAfterData to do the actual saving for us. Please note in the above example you may want to pass the context or to declare it globally instead to be more efficient.  
So far the above solution appears to be working for me, with no version conflicts 

Tuesday, July 21, 2015

Create Embedded Video / External Link Video using managed CSOM

We have been introduced to two new ways of adding videos to SharePoint in the latest version of SP i.e. SP 2013:
1. Embedded videos
2. Link to video.
Following is the code to add these videos programmatically using CSOM(client side object model)


using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Video;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Upload_Videos
{
  class Program
  {
     static void Main(string[] args)
     {
        string _siteUrl = ""// url of site
        string _videoTitle = ""// title of video
        string _embeddedCode = ""// embedded code for video
        string _externalLink = ""// link for video
        using (ClientContext context = new ClientContext(_siteUrl))
        {
           try
           {
               Web _web = context.Web; // gets web object of current context
               List _videoList = _web.Lists.GetByTitle("Assets"); // gets object of library where we need to add video , make sure that this library has option to add video content type
               context.Load(_videoList.RootFolder); 
               context.ExecuteQuery();
               var _contentType = GetContentType(context, _videoList, "Video");// gets content type ID for video 
               var _contentTypeId = _contentType.Id;
               VideoSet.CreateVideo(context, _videoList.RootFolder, _videoTitle, _contentTypeId); // creates video with the name stored in _videoTitle
               context.ExecuteQuery();
               Microsoft.SharePoint.Client.Folder Ofile = context.Web.GetFolderByServerRelativeUrl(_videoList.RootFolder.ServerRelativeUrl + "/" + _videoTitle);
               context.Load(Ofile.ListItemAllFields);
               context.ExecuteQuery();
               Ofile.ListItemAllFields["Title"] = _videoTitle;
               Ofile.ListItemAllFields["VideoSetEmbedCode"] =_embeddedCode;
               //Ofile.ListItemAllFields["VideoSetExternalLink"] = _externalLink; //to set link for video
               Ofile.ListItemAllFields.Update();
               context.ExecuteQuery();
               Console.WriteLine("Video created!");
           }
           catch (Exception ex)
           {
              Console.WriteLine(ex.ToString());
           }
           Console.ReadLine();
        }
   }
   static private ContentType GetContentType(ClientContext ctx, List lst, string contentType)
   {
       ContentTypeCollection listContentTypes = lst.ContentTypes;
       ctx.Load(listContentTypes, types => types.Include(type => type.Id, type => type.Name,type => type.Parent));
       var result = ctx.LoadQuery(listContentTypes.Where(c => c.Name == contentType));
       ctx.ExecuteQuery();
       ContentType targetContentType = result.FirstOrDefault(); 
       return targetContentType;
   }
  }
}

Change welcome menu text in SharePoint 2013

Make SharePoint more user friendly by giving your users a friendly welcome message. We will use jquery to achieve this. Make sure to include jquery file . You can download it from Jquery Download Link

<script>
$(document).ready(function (){

 var _welcomemenu =  $("a[title='Open Menu']")
        .clone()    //clone the element
        .children() //select all the children
        .remove()   //remove all the children
        .end()  //again go back to selected element
        .text();    //get the text of element
      var _newwelcomemenu = _welcomemenu.split(',')[1]  +" " + _welcomemenu.split(',')[0]; //swipe first and last name

       $("a[title='Open Menu']").text( "Welcome " + _newwelcomemenu); // Add friendly text

});
</script>

Breadcrumb navigation in SharePoint 2013

Here is a quick way to add bread crumb navigation to SharePoint 2013 . By default we can only see the current location . To get a navigation which drills down to parent site collection , i usually go with the following approach. 

ListSiteMapPath  creates a SiteMapPath control that renders as site map as a hierarchical set instead of nested un-ordered lists.


Both of the controls have the “SiteMapProvider” property. ListSiteMapPath has a property “SiteMapProviders” which means we can specify more than one  provider object with one ListSiteMapPath control. There are many providers for us to choose. Following articles describes it well http://www.ktskumar.com/blog/2008/04/sharepoint-navigation-providers-part-1/ :

• SPSiteMapProvider
• SPContentMapProvider
• SPXmlContentMapProvider
• CurrentNavSiteMapProvider
• CurrentNavSiteMapProviderNoEncode
• CombinedNavSiteMapProvider
• GlobalNavigation
• CurrentNavigation
• ExtendedSearchXmlContentMapProvider
• AdministrationQuickLaunchProvider
• SharedServicesQuickLaunchProvider
• PWASiteMapProvider
• GlobalNavSiteMapProvider
• SiteDirectoryCategoryProvider
• MySiteMapProvider
• MySiteLeftNavProvider
• MySiteSubNavProvider
• SPNavigationProvider


Add following code snippet to any content place holder in master page where we want breadcrumb navigation to appear, in my case i added it in "PlaceHolderPageTitleInTitleArea" .

<!--SPM:<SharePoint:ListSiteMapPath
                runat="server"
                SiteMapProviders="CurrentNavSiteMapProviderNoEncode"
                RenderCurrentNodeAsLink="false"
                PathSeparator=""
                CssClass="ms-breadcrumb"
                NodeStyle-CssClass="ms-breadcrumbNode"
                CurrentNodeStyle-CssClass="ms-breadcrumbCurrentNode"
                RootNodeStyle-CssClass="ms-breadcrumbRootNode"
                NodeImageOffsetX="0"
                NodeImageOffsetY="289"
                NodeImageWidth="16"
                NodeImageHeight="16"
                NodeImageUrl="/_layouts/15/images/fgimg.png?rev=23"
                RTLNodeImageOffsetX="0"
                RTLNodeImageOffsetY="312"
                RTLNodeImageWidth="16"
                RTLNodeImageHeight="16"
                RTLNodeImageUrl="/_layouts/15/images/fgimg.png?rev=23"
                HideInteriorRootNodes="true"
                SkipLinkText=""/>-->
Adding above results in the following





Now, instead of i wanted as bread crumb separator. 
                NodeImageOffsetX="0"
                NodeImageOffsetY="289" change to 573
                NodeImageWidth="16" 
                NodeImageHeight="16" change to 10
                NodeImageUrl="/_layouts/15/images/fgimg.png?rev=23" Image used for separator


Making above changes did the trick for me . After above changes our navigation is supposed to look like this.







Adding the following css aligns breadcrumb items in a horizontal menu 

.ms-breadcrumb li,ul {
 display: inline;  margin-left:-2.5em   
}




.ms-breadcrumb > li:first-child  > span:first-child{
display:none
}

Above hides image separator at the beginning 





Following css can be overridden to change the way we want our navigation to appear
               CssClass="ms-breadcrumb"
                NodeStyle-CssClass="ms-breadcrumbNode"
                CurrentNodeStyle-CssClass="ms-breadcrumbCurrentNode"
                RootNodeStyle-CssClass="ms-breadcrumbRootNode"

Add publishing columns to content types, lists, or libraries


A column type determines how data is stored and displayed in a list or library. You choose a column type when you create a column, and the type that you choose indicates the type of data that you want to store in the column—for example, numbers only, formatted text, a list of choices, or a number that is calculated automatically. SharePoint Online includes three column types unique to publishing sites that enable you to control how rich text, images, and hyperlinks are handled on site pages, lists, and libraries. This article explains how to use them.

Publishing column types

SharePoint Online includes three publishing column types. Each is explained in the following table.
Full column nameShort nameDescription
Full HTML content with formatting and constraints for publishingPublishing HTMLEnables rich text authoring by specifying font, styles, links, tabular layouts, reusable content, and so on. Helps you manage authoring options by placing content field controls within SharePoint page layouts. You can apply content and formatting constraints to ensure that new content appears according to pre-established styles and themes.
Image with formatting and constraints for publishingPublishing ImageEnables you to provide metadata about the images used on your site. This metadata includes image path, alt text, hyperlink, image alignment, thumbnail, and rendering size. Provides an easier way for users to add images than manually entering the URL of the image or web reference. You can tighten control over images when you place content field controls within SharePoint page layouts.
Hyperlink with formatting and constraints for publishingPublishing HyperlinkEnables authors to browse to an object and link to it rather than typing a URL. Also provides the option of entering display text and tooltip metadata. You set link formatting and other options when you place content field controls within SharePoint page layouts.
Note    Publishing must be enabled by a site collection administrator for these column types to appear. To enable publishing, go to Settings Small Settings gear that took the place of Site Settings.> Site Settings. Under Site Collection Administration, click Site collection features. Scroll down to SharePoint Server Publishing Infrastructure and click Activate.

Create the publishing column

Let’s start by creating a publishing column. We’ll use the Publishing Hyperlink column type for this example.
Note    You must be a site administrator or site collection administrator to add columns to lists and libraries.
  1. From the root of your site collection, click SettingsSmall Settings gear that took the place of Site Settings. > Site settings.
  2. Under Web Designer Galleries, click Site columns.
  3. Click Create.
  4. Give the column a name. For this example, we’ll use “Hyperlink.”
  5. In the list of column types, clickHyperlink with formatting and constraints for publishing.
  6. In the Existing group box, select Publishing Columns. This step is optional but can be helpful for organizing columns.
  7. Click OK.

Add a publishing column to a content type

Publishing columns based on the Publishing HTML and Publishing Image column types are already part of the Article Page, Enterprise Wiki Page, Project Page, and Welcome Page content types. Publish Hyperlink is not. In this procedure, we’ll add our new Hyperlink column, which is based on the Publishing Hyperlink column type, to a content type. The procedure is the same whether the content type is built-in or custom.
Note    You must be a site administrator or site collection administrator to add columns to lists and libraries.
  1. From the root of your site or site collection, click Settings > Site Settings.
  2. Under Web Designer Galleries, click Site content types.
  3. On the Site Content Types page, click the content type to which you want to add the column.
  4. At the bottom of the Site Content Type Information page, click Add from existing site columns.
  5. On the Add Columns page, in the Select columns from box, click Publishing Columns (or whatever group you chose above).
  6. In the Available columns list, click Hyperlink, and then click Add.
  7. Click OK.
Now your content type includes the Hyperlink column. When you edit the properties for any new page created from that content type, you’ll see the column listed.

Add a publishing column to a list or library

Adding a publishing column to a list or library is very similar to adding one to a content type. In this example, we’ll continue to use the Hyperlink column that we created in the first procedure. Whether the list or library is built-in or custom, the procedure is exactly the same.
Note    You must be a site administrator or site collection administrator to add columns to lists and libraries.
  1. Navigate to your list or library.
  2. On the List tab, in the Settings group, click List Settings. (If you’re adding the column to the library, click Library Settings.)
  3. Under Columns, click Add from existing site columns.
  4. In the Select site columns from box, click Publishing Columns (or whatever group you chose above).
  5. In the Available site columns list, click Hyperlink, and then click Add.
  6. Click OK

Thursday, July 16, 2015

URL to Open in Model Popup with IsDlg

Here’s what the URL may look like:
1
/sites/lozzi/Lists/Events/DispForm.aspx?ID=4&IsDlg=1
Note the IsDlg=1 at the end. I’m a big fan in questioning everything, so let’s try it out for ourselves.
Go to a display form of a list item, which is not in a dialog.
A Normal Display Form
and add &IsDlg=1 (case sensitive) to the end of the URL, hit enter.
Display Form with IsDlg=1

Deleting Multiple Columns using ECMA

function DeleteCrossChargeTransactions(vReferenceId)
{
    try
        {
           
            var contextDelete = new SP.ClientContext.get_current();
            var webDelete = contextDelete.get_web();
            var listDelete = webDelete.get_lists().getByTitle('Cross Charge Details NA');
            var queryDelete = new SP.CamlQuery();
           
            queryDelete.rowLimit=100;
            queryDelete.set_viewXml('<View Scope=\'RecursiveAll\'><Query><Where><Eq><FieldRef Name="Reference_x0020_Id" /><Value Type="Text">'+vReferenceId+'</Value></Eq></Where></Query></View>');
            // add your query to the getItems command for your list
           this.collListItemsDelete = listDelete.getItems(queryDelete);
           
             // issue the load command, these can be batched for multiple operations
             contextDelete.load(collListItemsDelete);
             // execute the actual query against the server, and pass the objects we have created to either a success or failure function
             contextDelete.executeQueryAsync(Function.createDelegate(this, this.SuccessFunction), Function.createDelegate(this, this.failed));
                                    
        }
    catch(e)
        {
           
        }
}

function SuccessFunction()
{
    var itemsToDelete = new Array();
    var count = this.collListItemsDelete.get_count();
    //alert("Rc count:"+ count);
   var listEnumerator = this.collListItemsDelete.getEnumerator();
   var contextDelete= new SP.ClientContext.get_current();
    while (listEnumerator.moveNext()) {
       var item = listEnumerator.get_current();
     // var s=item.get_item("Reference_x0020_Id");
       itemsToDelete.push(item);
   }    
   for (var i = itemsToDelete.length-1; i >= 0 ; i--) {
       itemsToDelete[i].deleteObject();
       //alert('Deleted:'+ i);
   }

   contextDelete.executeQueryAsync(Function.createDelegate(this, this.allItemsDeleted), Function.createDelegate(this, this.failed));
   
}