Tuesday, December 22, 2015

SharePoint 2013–Update password of a managed account using PowerShell

We can change the password of a SharePoint managed account from SharePoint central administration console as well as using PowerShell.

Fun fact here is that, we don’t need to do anything from our Domain Controller. When we update the password either from Central Administration or PowerShell, it will automatically update the user’s password in Active Directory.

Update managed account password in Central Administration

image

image

Update managed account password using PowerShell

Set-SPManagedAccount -identity SP\SP_Farm -NewPassword (Convertto-Securestring "mynewpassword" -AsPlainText -Force) -SetNewPassword

Thursday, December 10, 2015

Set home directories for multiple clients in CygWin SFTP–Windows Server 2012 R2

In one of my SharePoint environments, we have a file server configured with SFTP. SFTP is used to securely transfer content from each user using their comfortable FTP clients. Those content will later be processed by custom tool and uploaded to analysis server for data analysis.

We have configured SFTP using CygWin.

The environment need to support multiple clients where each client has multiple users. Following were some requirements

  • Separate drive (“F”) to store uploaded content
  • Each client should have a folder under SFTP root data directory
  • Each user should have a separate folder under his client
  • Specific user can interact with his own content only
  • Specific user can’t view other users folders

Following were the steps I used to configure home directories and their permissions

1. Create domain user groups in Domain Controller

image7

image9


2. Add users to respective user groups (clients)

image14

image20

3. Perform configurations in SFTP to specify home directories for each client

    • Navigate to /etc/fstab file
    • Specify home directories as given below

image

4. Open CygWin Shell and execute following commands to obtain user information

image30

5. Change home directories of users in /etc/passwd file as shown below

image39

6. Create home directories in F drive and create folders for each client

image

7. Create sub folders for each users within the client

image

8. Deny permissions for SFTP user groups in CygWin root directory

image53

image59

9. Deny permission for SFTP user groups in SFTP root directory (F:/FTP)

image69

10. Now we need to provide permission to specific user folders. We need to disable permission inheritance as shown below

image74

image79

image

image89

image96

image101

image105

11.  Check using FTP client

image

If we try to browse the directory in client level, an error message will be displayed

image

That is the expected behavior. that means the a specific user can interact with his own content only.

Wednesday, November 18, 2015

Enable “BI Semantic Model Connection” content type in SharePoint 2013 site collection

In one of my SharePoint environments, I had configured PowerPivot for SharePoint service application.

My requirement was to create a new client site collection and create a BISM connection to my analysis server. Finally my users consume that connection to analyze content using Microsoft Excel.

When I create a document library to create my BISM model, I couldn’t find the required content type. Following were the steps I used.

1. Navigate to PowerPivot for SharePoint 2013 Configuration

image

image

2. Select the new site collection to enable BISM and click run to enable necessary services.

image

3. Create a document library in that site and allow BISM content type

image

image

image

image

image

image

Tuesday, October 6, 2015

Collab365 “Low-Trust Provider Hosted Apps for On-Premise SharePoint 2013”

speakers-badgeCollab365 Global Conference is set to be this years BIGGEST and most wide ranging event yet. With over 120 speakers.

There are sessions ranging from SharePoint, Office365, Windows Azure and  business implementation of these technologies.

Best part of this conference is that it is Free and it is Online :)

 

 

 

I’ll be covering a session on Low-trust provider hosted apps for on-premise SharePoint 2013.

In this session I will show how to configure On-Premise SharePoint 2013 environment, to consume Low-Trust Provider-Hosted apps created as Azure Web Site/Cloud Service.This allows us to decouple applications from SharePoint, and reap all the benefits of Platform as a Service (PaaS) provided by Windows Azure Websites.

You will learn

  • Provider-Hosted app authentication/authorization models
  • Use Office 365 tenant to make ACS as our trust provider
  • How to create sample low-trust provider hosted app for on-premise environment
  • Use azure web site/cloud service to host the app (MVC web application)

Sunday, September 13, 2015

SharePoint 2013 Resolving the error - Microsoft.Office.Server.UserProfiles.UserProfileApplicationNotAvailableException: UserProfileApplicationNotAvailableException_Logging :: UserProfileApplicationProxy.ApplicationProperties ProfilePropertyCache does not have <GUID>

Recently I received above error when accessing user profile service’s REST services to access profile information. following are the steps I used to overcome the error

Step 1

Go to IIS Manager and select Authentication

image

Step 2

Select Anonymous authentication

image

Step 3

Select Edit

image

Step 4

Select Specific User and provide “IUSR” without any password

image

Tuesday, September 1, 2015

SharePoint 2013 - Remove faulty Cache Host which is in “Unknown” state from a cache cluster

One of my SharePoint environments had some servers removed from that farm. Unfortunately they were not removed properly where, some configuration items still remained.For an example, my distributed cache service prompted me with multiple errors due to invalid configuration.

When I try to remove the unresponsive CacheHost, using PowerShell “Unregister-CacheHost” it gave me following error
Unregister-CacheHost : ErrorCode<UnspecifiedErrorCode>:SubStatus<ES0001>:No such host is known
When I execute the simplest command Get-CacheHost, that too gave me an error
image
I tried to unregister or remove the Unknown host using PowerShell commands but i was not that lucky.

Finally SharePoint gods decided to let me finish my task :)
I found a solution using “Export-CacheClusterConfig” and “Import-CacheClusterConfig” commands

Following are the steps I follow
Use-CacheCluster
Export-CacheClusterConfig -File "F:\Cache\CacheConfig.xml"
My configuration file had the hosts section as below. I removed the faulty host from the config file.

  1. <hosts>
  2.   <host replicationPort="22236"arbitrationPort=22235clusterPort=22233 hostId=99833size=800leadHost=true account=dev\svcuser
  3.       cacheHostName=AppFabricCachingService name=testserver1
  4.       cachePort=22233 />
  5.   <host replicationPort=22236arbitrationPort=22235clusterPort=22233 hostId=34355size=400leadHost=true account=dev\svcuser
  6.       cacheHostName=AppFabricCachingService name=testserver2
  7.       cachePort=22233 />
  8.   <host replicationPort=22236arbitrationPort=22235clusterPort=22233 hostId=67893size=400leadHost=true account=dev\svcuser
  9.          cacheHostName=AppFabricCachingService name=testserver3
  10.          cachePort=22233 />
  11. </hosts>

Then I ran the following command
Import-CacheClusterConfig -File "F:\Cache\CacheConfig.xml"
That solved my problems.

Wednesday, July 15, 2015

Presentation–Introduction to Office365 API

Recently I did a session on Introduction to Office365 API at SharePoint Sri Lanka forum.

July

Following is the presentation I did

Sunday, June 21, 2015

How to use JQuery Deferred Objects in asynchronous operations for SharePoint JSOM

When we write SharePoint apps or client side logic in SharePoint pages, we may use asynchronous operations using JSOM or REST. As operations are asynchronous it is hard to control the flow of the execution. Apart from that we can make the situation even harder if we use nested asynchronous operations.

For an example I will read a web property using a REST call and the result is taken as an input to another REST call to read a list item.

Since my operations are asynchronous, how do I make sure that I enter to the second method once I return from the first method. It is difficult, right?

As a solution we can use JQuery Deferred objects. Following is a sample method to retrieve quick launch navigation links using deferred objects.

  1. function ReadNavigation() {
  2.     var def = $.Deferred();
  3.  
  4.     SP.SOD.executeOrDelayUntilScriptLoaded(function () {
  5.         var clientContext = new SP.ClientContext.get_current();
  6.         var quickLnch = clientContext.get_web().get_navigation().get_quickLaunch();
  7.         clientContext.load(quickLnch);
  8.  
  9.         clientContext.executeQueryAsync(
  10.             function () {
  11.                 var qlEnum = quickLnch.getEnumerator();
  12.                 var currentNav = [];
  13.                 while (qlEnum.moveNext()) {
  14.                     var node = qlEnum.get_current();
  15.                     var ob = {};
  16.                     var title = node.get_title();
  17.                     var url = node.get_url();
  18.  
  19.                     ob.title = title;
  20.                     ob.url = url;
  21.                     currentNav.push(ob);
  22.                 }
  23.                 def.resolve(currentNav);
  24.             }, function (sender, args) {
  25.                 def.reject("error");
  26.             });
  27.     }, "SP.js");
  28.     return def;
  29. }
  30.  
  31. ReadNavigation().done(function (nav) {
  32.     console.log(nav)
  33. }).fail(function (nav) { console.log("error!") })

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 :)

Tuesday, May 26, 2015

Presentation-Introduction to SharePoint Online at Kaatsu International University

Recently I did a presentation on “Introduction to SharePoint Online” at Kaatsu International University, Sri Lanka. I conducted few demos as well as implementation guidelines. Following is the presentation I used

Saturday, May 2, 2015

Resolving System.Threading.ThreadAbortException: Thread was being aborted error when performing a long time consuming operation in a page from Layouts folder

Sometimes we use SharePoint application pages or web parts to perform heavy operations those take long time than usual. In that case we have to explicitly increase the timeout periods using the Web.Config file.

Recently I came across a similar situation where an application page is used to create site collections using custom definitions.To resolve the error I modified the Web.Config file of the relevant web application and provided following values.

  1. <httpRuntime maxRequestLength="2097152" executionTimeout="1500" requestLengthDiskThreshold="8192" shutdownTimeout="108000" />

Unfortunately that remedy didn’t work.

After few hours of struggling it occurred to me that I was modifying the wrong Web.Config file. Since my page is located in Layouts folder, the page is not directly bound to a specific web application. For these kind of situations we are provided with a different Web.Config file, which is located under the Layouts folder.

image

I had to do following modification to increase the timeout period under system.web section

  1. <httpRuntime executionTimeout="1500" />

That fixed my issue.