Monday, July 27, 2015

Create default associated groups for Sharepoint site collection via client object model (CSOM)

When you create site collection via CSOM default Sharepoint groups (Visitors, Members and Owners) are not created by default. So when you will go to Site settings > People and groups most probably you will only see Everyone and Excel Services Viewers:

image

In order to create these groups you need to write additional code by yourself. Here it is:

   1: using (var ctx = new ClientContext("http://example.com"))
   2: {
   3:     ctx.Load(ctx.Web);
   4:     ctx.ExecuteQuery();
   5:     CreateDefaultAssociatedGroups(ctx);
   6: }
   7:  
   8: private static void CreateDefaultAssociatedGroups(ClientContext ctx)
   9: {
  10:     EnsureDefaultAssociatedGroup(ctx, GetGroupName(ctx, "Visitors"),
  11:         String.Format("Use this group to grant people read permissions to the SharePoint site: {0}",
  12:             ctx.Web.Title));
  13:     EnsureDefaultAssociatedGroup(ctx, GetGroupName(ctx, "Members"),
  14:         String.Format("Use this group to grant people contribute permissions to the SharePoint site: {0}",
  15:             ctx.Web.Title));
  16:     EnsureDefaultAssociatedGroup(ctx, GetGroupName(ctx, "Owners"),
  17:         String.Format("Use this group to grant people full control permissions to the SharePoint site: {0}",
  18:             ctx.Web.Title));
  19: }
  20:  
  21: private static void EnsureDefaultAssociatedGroup(ClientContext ctx, string groupName,
  22:     string description)
  23: {
  24:     if (string.IsNullOrEmpty(groupName))
  25:     {
  26:         return;
  27:     }
  28:  
  29:     if (GroupExist(ctx, groupName))
  30:     {
  31:         return;
  32:     }
  33:     var ci = new GroupCreationInformation
  34:     {
  35:         Title = groupName,
  36:         Description = description
  37:     };
  38:     var group = ctx.Web.SiteGroups.Add(ci);
  39:     var roleType = RoleType.None;
  40:     if (groupName.EndsWith("Visitors"))
  41:     {
  42:         ctx.Web.AssociatedVisitorGroup = group;
  43:         ctx.Web.AssociatedVisitorGroup.Update();
  44:         roleType = RoleType.Reader;
  45:     }
  46:     else if (groupName.EndsWith("Members"))
  47:     {
  48:         ctx.Web.AssociatedMemberGroup = group;
  49:         ctx.Web.AssociatedMemberGroup.Update();
  50:         roleType = RoleType.Contributor;
  51:     }
  52:     else if (groupName.EndsWith("Owners"))
  53:     {
  54:         ctx.Web.AssociatedOwnerGroup = group;
  55:         ctx.Web.AssociatedOwnerGroup.Update();
  56:         roleType = RoleType.Administrator;
  57:     }
  58:  
  59:     ctx.Web.Update();
  60:     ctx.ExecuteQuery();
  61:  
  62:     if (roleType != RoleType.None)
  63:     {
  64:         var roleDefinition = ctx.Web.RoleDefinitions.GetByType(roleType);
  65:         ctx.Load(roleDefinition);
  66:         ctx.ExecuteQuery();
  67:  
  68:         var roleDefinitions = new RoleDefinitionBindingCollection(ctx);
  69:         roleDefinitions.Add(roleDefinition);
  70:         ctx.Web.RoleAssignments.Add(group, roleDefinitions);
  71:         ctx.ExecuteQuery();
  72:     }
  73: }
  74:  
  75: private static string GetGroupName(ClientContext ctx, string suffix)
  76: {
  77:     return String.Format("{0} {1}", ctx.Web.Title, suffix);
  78: }
  79:  
  80: private static bool GroupExist(ClientContext ctx, string groupName)
  81: {
  82:     var groups = ctx.Web.SiteGroups;
  83:     ctx.Load(groups);
  84:     ctx.ExecuteQuery();
  85:     foreach (var g in groups)
  86:     {
  87:         if (g.Title == groupName)
  88:         {
  89:             return true;
  90:         }
  91:     }
  92:     return false;
  93: }

After executing this code you will see 3 additional groups in Site settings > People and groups:

image

If you will go to Site settings > Site permissions you will find that Visitors group has Read permissions on the site, Members – Contribute and Owners – Full control:

image

Hope that this information will help you.

Fix problem with empty page layouts when edit page in Sharepoint

When you edit publishing page in Sharepoint and click Page layouts button on the ribbon you may face with the following problem: list with available page layouts won’t be opened. Instead the only thing which you will see is popup message “Apply a new layout to this page”.

One of possible reason may be the following. If you will open browser console at developer tools and click on the ribbon button again, the following error will be shown:

Unable to get property nodeName of undefined or null reference

In this case go to Site settings > Page layouts and site templates. Here in Page layouts section instead of “Pages inherit preferred layouts from parent site” select “Pages in this site can only use the following layouts”. In the left list of layouts select all page layouts except those which start with “(Invalid Associated Content Type)”. Also check that in the right list of selected layouts there are no such page layouts. After that click Save. If you now go to the publishing page, refresh it and edit you will find that list of available page layouts is now shown.

Another solution which may be useful if you need to allow using of all page layouts is to check all page layouts which have “(Invalid Associated Content Type)” prefix, then check their urls (it can be done by inspecting left and right lists of page layouts in Site settings > Page layouts and site templates in developer tools: values of options elements of these lists contain urls of the pages in /_catalogs/masterpage so you will easily find them), then go to Site settings > Master pages and page layouts, find appropriate page layouts and change their content type to the correct ones.

Wednesday, July 22, 2015

Problem with X-Frame-Options: SAMEORIGIN in Yammer embedded feed

When you add Yammer embed feed to your Sharepoint Online site you may have the following problem: if user didn’t complete Yammer signup then instead of the feed he or she will see empty box inside iframe. To be more precise you may face with this problem if your site is running on any other platform or technology, but as I faced with it on Sharepoint Online, I decided to mentioned it here.

Let’s see it in more details. Embedded feed is loaded using the following code:

   1: <script src="https://c64.assets-yammer.com/assets/platform_embed.js"
   2: type="text/javascript"></script>
   1:  
   2: <div id="embedded-feed" style="height:800px;"></div>
   3: <script type="text/javascript">
   4:     yam.connect.embedFeed({ container: '#embedded-feed', network: 'example.com'});
</script>

When user has active Yammer profile it shows the Yammer feed in iframe which is added into div with specified identifier. However when user doesn’t have profile yet, empty page will be shown. The reason of this problem is the following: when user didn’t complete Yammer signup and iframe loads feed page which URL looks like this:

https://www.yammer.com/platform_embed/feed?container=%23embedded-feed&network=example.com&network_permalink=example.com&bust=…

(instead of example.com you will have your domain here and bust will have identifier which corresponds to the current user), it redirects user to the following page:

https://www.yammer.com/example.com/signup/complete_signup?activation_code=…&user_id=…

One of the HTTP headers returned in response for this page is the following:

X-Frame-Options: SAMEORIGIN

This header tells to the browser that remote site can’t be loaded in iframe if parent page belongs to different domain (different from yammer.com).

In order to tell user about that he or she should complete signup first the following script may be used:

   1:  
   2: var g_YammerCompleteSignup_CompletePolling = false;
   3: function YammerCompleteSignup(){
   4:     if (jQuery("iframe#embed-feed").length) {
   5:         try {
   6:             var url = jQuery("iframe#embed-feed").attr("src");
   7: jQuery("#embedded-feed").prepend("<span style='background: url(/_layouts/images/info16by16.gif) " +
   8:     "no-repeat; display: inline-block; background-size: 16px 16px; height: 16px; " +
   9:     "padding-left: 18px;'><span style='font-size: 12px; height: 16px; display: table-cell; " +
  10:     "vertical-align: middle;'>Yammer feed will be empty if you don't have Yammer account yet. " +
  11:     "Click <a href='" + url + "' target='_blank'>here</a> in order to complete signup and then " +
  12:     "refresh this page.</span></span>");
  13:         } finally {
  14:             g_YammerCompleteSignup_CompletePolling= true;
  15:         }
  16:     }
  17:     
  18:     if (g_YammerCompleteSignup_CompletePolling) {
  19:         return;
  20:     }
  21:  
  22:     setTimeout(YammerCompleteSignup, 2000);
  23: }

It starts polling the DOM and when iframe is added by yam.connect.embedFeed() call, it reads its src and uses it when add informational notice above the iframe, which looks like this:

image

The drawback of this approach is that this text will be shown both for users which already completed signup and for users which didn’t do that.

Monday, June 15, 2015

Create reminder workflow for Sharepoint Online which send emails when content is outdated

On the production sites it is often needed to monitor that content of appropriate pages is kept up to date. One of the way to do it is to create workflow which will check Modified date of the page and if it is older than specified time interval, sends email to responsible users. It can be done with custom workflow created in Sharepoint Designer 2013, but it is important to use “Sharepoint 2013 workflow” as template, because “Sharepoint 2010 workflow” don’t have very useful Go to action which will be needed in order to start workflow once and run it by the loop (by default in Sharepoint Online only 2010 workflows are enabled. See Enable Sharepoint 2013 workflows in Sharepoint Online post which shows how to enable 2013 workflows in Sharepoint Online).

In order to create such workflow we need to go to Sharepoint Designer 2013 and choose Workflows > List workflow. In the opened window specify workflow name and the platform:

image

In the workflow designer first of all we need to create workflow local variable called Past with Date/Time type:

image

Then create stages with actions like shown below:

image

Let’s go through stages one by one. First stage is called Initialization. Here we subtract 12 months from current date, store result to workflow local variable called Past and go to next stage called Check content and notify. In this stage we check whether Modified date of the current list item (page) is less than Past variable (current date minus 12 month). If yes, we send email with link on the item to the specific user:

image

Adding link on the current item to notification email in workflow is little bit tricky so I will also show how link is created:

image

I.e. we need to use Current Item URL from Workflow Context for link address.

On the last stage we wait for 30 days and repeat from stage 1. As result it will be enough to start workflow once for particular page (it can be done e.g. by selecting page in default doclib view and choose Advanced > Workflows in the context menu) and it will run by itself after that without need to restart it manually each month. This is one of the advantages of having Go to action.

Enable Sharepoint 2013 workflows in Sharepoint Online (Office 365)

In Sharepoint Online you may face with the problem when will try to create workflow in Sharepoint Designer 2013: only “Sharepoint 2010 workflow” will be available in Platform Type dropdown list, while new “Sharepoint 2013 workflow” won’t be there:

image

The problem will be more unclear after you will check in admin center’s settings page that “Block Sharepoint 2013 workflows” checkbox is unchecked:

image

In order to enable “Sharepoint 2013 workflows” in Office 365 go to Site settings > Workflow settings:

image

“Workflow Health” in parenthesis is actual a link (very hidden link which looks like text. This is for sure should be fixed by MS somehow). Click on it and you will see the following page:

image

On this page click Activate button. After that when you will reconnect to the site in Sharepoint Designer you should see “Sharepoint 2013 workflow” option in the list of available platforms:

image

Please note that this configuration should be made per SPWeb. I.e. if you activated it only on the root site, it won’t be available on the sub sites. You will need to activate it on sub sites where you need Sharepoint 2013 workflow separately. Hope that this information will help you.

Friday, June 12, 2015

Fix Operation is not valid due to the current state of an object when call ashx handler in Sharepoint context

Custom ashx handler may be useful in Sharepoint e.g. when we need to return some server data in json format in order to consume it on the client side. In this case we most probably copy ashx to subfolder of 14 or 15/Layouts folder and call it in context of Sharepoint site: http://example.com/_layouts/foo/bar.ashx. If in codebehind of ashx handler we need to reopen site under elevated privileges like shown below:

   1: SPSecurity.RunWithElevatedPrivileges(
   2:     () =>
   3:         {
   4:             using (var site = new SPSite(SPContext.Current.Site.ID,
   5:                 SPContext.Current.Site.Zone))
   6:             {
   7:                 using (var web = site.OpenWeb(SPContext.Current.Web.ID))
   8:                 {
   9:                     ...
  10:                 }
  11:             }
  12:         });

we may get exception:

Operation is not valid due to the current state of an object

In order to avoid it we need to use slightly different code:

   1: var currentSite = SPContext.Current.Site;
   2: var currentWeb = SPContext.Current.Web;
   3: SPSecurity.RunWithElevatedPrivileges(
   4:     () =>
   5:         {
   6:             using (var site = new SPSite(currentSite.ID, currentSite.Zone))
   7:             {
   8:                 using (var web = site.OpenWeb(currentWeb.ID))
   9:                 {
  10:                     ...
  11:                 }
  12:             }
  13:         });

Hope that it will help someone.

Monday, June 8, 2015

Change date time fields format in Sharepoint from friendly to standard view

In Sharepoint 2013 when you create new list item or upload document to the doclib, OTB Created and Modified fields will show “A few seconds ago” text instead of actual date time (or e.g. “N minutes ago”):

image

In order to show date time in them we need to change format from Friendly to Standard mode. It is done in List settings > Field settings:

image

After that date time columns will show actual value:

image