Tuesday, June 16, 2015

Centrally manage reusable content in SharePoint Online. Publish reusable content to publishing sites using SPServices

Reusable Content is a great feature in SharePoint. We can create html snippets in our publishing sites and we can reuse them in any page (mostly wiki pages).

image  image

This is a good place to start if you don’t have a good understanding about the concept

Problem

Let’s take a real world example with hundreds of SharePoint publishing sites. If we use out of the box reusable content features, we have to manually add html content to each and every site collection. This is because the default boundary of the feature is Site Collection. I guess you can see the problem. If I need to update some common content I have to update that in all the places. Not a good solution, right?

Solution

As the solution, I create an authoring site collection and add my reusable content there.

image

Then I created an application page which I use to copy the content to all selected publishing site collections.

Since I’m using SharePoint Online I used a sandbox solution to deploy pages and I used SPServices to copy the content. To retrieve all site collections, I used SharePoint search REST API.

  1. <script type="text/javascript">
  2.  
  3.         $(document).ready(function () {
  4.             searchSites();
  5.         });
  6.  
  7.         function closeInProgressDialog() {
  8.             if (dlg != null) {
  9.                 dlg.close();
  10.             }
  11.         }
  12.  
  13.  
  14.         function showInProgressDialog() {
  15.             if (dlg == null) {
  16.                 dlg = SP.UI.ModalDialog.showWaitScreenWithNoClose("Please wait...", "Please wait...", null, null);
  17.             }
  18.         }
  19.  
  20.         var TrackCalls = { "callCount": 0, "receivedCount": 0, "errorCount": 0 };
  21.  
  22.         function freezeui() {
  23.             $.blockUI.defaults.css = {
  24.                 padding: 0,
  25.                 margin: 0,
  26.                 width: '30%',
  27.                 top: '40%',
  28.                 left: '35%',
  29.                 backgroundColor: '#fff',
  30.                 textAlign: 'center',
  31.                 cursor: 'wait'
  32.             };
  33.  
  34.             TrackCalls.callCount = 1;
  35.             TrackCalls.receivedCount = 0;
  36.             TrackCalls.errorCount = 0;
  37.             $.blockUI({ message: '<img src="Preloader_3.gif" /><h1>Just a moment...</h1><p>Deploying to all publishing sites</p>' });
  38.             callBack();
  39.         }
  40.  
  41.         function freezeuiLoad() {
  42.  
  43.             $.blockUI.defaults.css = {
  44.                 padding: 0,
  45.                 margin: 0,
  46.                 width: '30%',
  47.                 top: '40%',
  48.                 left: '35%',
  49.                 backgroundColor: '#fff',
  50.                 textAlign: 'center',
  51.                 cursor: 'wait'
  52.             };
  53.             TrackCalls.callCount = 1;
  54.             TrackCalls.receivedCount = 0;
  55.             TrackCalls.errorCount = 0;
  56.             $.blockUI({ message: '<img src="Preloader_3.gif" /><h1>Just a moment...</h1><p>Loading all publishing sites</p>' });
  57.             callBack();
  58.         }
  59.  
  60.  
  61.         function callBack() {
  62.             setTimeout(function () {
  63.  
  64.                 if (TrackCalls.callCount == TrackCalls.receivedCount) {
  65.                     $.unblockUI();
  66.                 }
  67.                 else if (TrackCalls.errorCount > 0) {
  68.                     $.unblockUI();
  69.                 }
  70.                 else {
  71.                     callBack();
  72.                 }
  73.             }, 2000);
  74.         }
  75.  
  76.  
  77.  
  78.  
  79.         function IsItemExist(web, list, title) {
  80.             var currentId;
  81.             $().SPServices({
  82.                 debug: true,
  83.                 operation: "GetListItems",
  84.                 async: false,
  85.                 webURL: web,
  86.                 listName: list,
  87.                 CAMLQuery: "<Query><Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + title + "</Value></Eq></Where></Query>",
  88.                 CAMLRowLimit: 1,
  89.                 completefunc: function (xData, Status) {
  90.                     $(xData.responseXML).SPFilterNode("z:row").each(function () {
  91.                         var currentItem = $(this);
  92.                         currentId = currentItem.attr("ows_ID");
  93.  
  94.                     });
  95.                 }
  96.             });
  97.  
  98.             return currentId;
  99.         }
  100.  
  101.         function searchSites() {
  102.             var dfd = $.Deferred(function () {
  103.                 freezeuiLoad();
  104.                 var url = _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?querytext='contentclass:sts_site'";
  105.                 $.ajax({
  106.                     url: url,
  107.                     method: "GET",
  108.                     headers: { "Accept": "application/json; odata=verbose" },
  109.                     success: function (data) {
  110.                         TrackCalls.receivedCount++;
  111.                         var query = data.d.query;
  112.                         var resultsCount = query.PrimaryQueryResult.RelevantResults.RowCount;
  113.                         var siteUrls = new Array()
  114.  
  115.                         var siteList = [];
  116.  
  117.                         for (var i = 0; i < resultsCount; i++) {
  118.                             var row = query.PrimaryQueryResult.RelevantResults.Table.Rows.results[i];
  119.                             var siteUrl = row.Cells.results[6].Value;
  120.                             var siteTitle = row.Cells.results[3].Value
  121.  
  122.                             var listStatus = IsListExist(siteUrl, "Reusable Content");
  123.                             if (listStatus != "error") {
  124.                                 siteUrls.push(siteTitle);
  125.                                 var ob = {};
  126.                                 ob.Url = siteUrl;
  127.                                 ob.Title = siteTitle;
  128.                                 siteList.push(ob);
  129.                             }
  130.                             else {
  131.                                 console.log("Not Found");
  132.                             }
  133.                         }
  134.                         dfd.resolve(siteList);
  135.  
  136.                         $("#publishingSites").datagrid({
  137.                             columns: [[
  138.                                 {
  139.                                     field: 'Title', title: 'Site Name', formatter: function (value, row, index) {
  140.                                         return '<a href="' + row.Url + '">' + value + '</a>';
  141.  
  142.                                     }
  143.                                 },
  144.                                     { field: 'Url', title: 'Site Url ' }
  145.                             ]],
  146.                             onCheck: function (rowIndex, rowData) {
  147.                                 var ro = rowIndex;
  148.                                 var aa = rowData;
  149.                            }
  150.                         });
  151.  
  152.                         var dg = $("#publishingSites").datagrid();
  153.                         dg.datagrid({ data: siteList });
  154.                     },
  155.                     error: function (data) {
  156.                         TrackCalls.errorCount++;
  157.                     }
  158.                 });
  159.             });
  160.             return dfd.promise();
  161.         }
  162.  
  163.         function IsListExist(web, list) {
  164.             TrackCalls.callCount++;
  165.             var listStatus = "error";
  166.             $().SPServices({
  167.                 debug: true,
  168.                 operation: "GetList",
  169.                 async: false,
  170.                 webURL: web,
  171.                 listName: list,
  172.  
  173.                 completefunc: function (xData, Status) {
  174.                     TrackCalls.receivedCount++;
  175.                     listStatus = Status;
  176.                 }
  177.             });
  178.             return listStatus;
  179.         }
  180.  
  181.         function escapeHtml(unsafe) {
  182.             return unsafe
  183.                  .replace(/&/g, "&amp;")
  184.                  .replace(/</g, "&lt;")
  185.                  .replace(/>/g, "&gt;")
  186.                  .replace(/"/g, "&quot;")
  187.                  .replace(/'/g, "&#039;");
  188.         }
  189.  
  190.         function CopyItems() {
  191.             freezeui();
  192.             $().SPServices({
  193.                 operation: "GetListItems",
  194.                 async: false,
  195.                 listName: "Reusable Content",
  196.                 completefunc: function (xData, Status) {
  197.                     TrackCalls.receivedCount++;
  198.  
  199.                     $(xData.responseXML).SPFilterNode("z:row").each(function () {
  200.                         var xitem = $(this);
  201.                         var myItem = xitem.attr("ows_Title");
  202.                         var reusableText = escapeHtml(xitem.attr("ows_ReusableHtml"));
  203.  
  204.                         var rows = $('#publishingSites').datagrid('getSelections');
  205.                         for (var i = 0; i < rows.length; i++) {
  206.                             var siteTitle = rows[i].Url;
  207.                             if (siteTitle != _spPageContextInfo.webAbsoluteUrl) {
  208.  
  209.                                 var id = IsItemExist(siteTitle, "Reusable Content", myItem);
  210.                                 if (id != undefined) {
  211.                                     //update
  212.                                     TrackCalls.callCount++;
  213.                                     $().SPServices({
  214.                                         operation: "UpdateListItems",
  215.                                         async: false,
  216.                                         webURL: siteTitle,
  217.                                         batchCmd: "Update",
  218.                                         listName: "Reusable Content",
  219.                                         ID: id,
  220.                                         valuepairs: [["Title", myItem], ["ReusableHtml", reusableText]],
  221.                                         completefunc: function (xData, Status) {
  222.                                             TrackCalls.receivedCount++;
  223.                                         }
  224.                                     });
  225.                                 }
  226.                                 else {
  227.                                     //add
  228.                                     TrackCalls.callCount++;
  229.                                     $().SPServices({
  230.                                         operation: "UpdateListItems",
  231.                                         async: false,
  232.                                         webURL: siteTitle,
  233.                                         batchCmd: "New",
  234.                                         listName: "Reusable Content",
  235.                                         valuepairs: [["Title", myItem], ["ReusableHtml", reusableText]],
  236.                                         completefunc: function (xData, Status) {
  237.                                             TrackCalls.receivedCount++;
  238.                                         }
  239.                                     });
  240.  
  241.                                 }
  242.                             }
  243.                         }
  244.                     });
  245.                 }
  246.             });
  247.         }
  248.  
  249.     </script>

Following is the end result. You have to select consuming publishing sites and click Sync button to publish updates

image

Hope this one helps someone :)

No comments: