Thursday, March 17, 2016

Query User Information List gives :Error 404 List Not found- SharePoint 2013

Being a SharePoint developer we all know that 
The User Information List stores information about a user by having some metadata set up for the user. Some examples are Picture, Email, DisplayName, LoginName etc. ) This list is only visible to and accessible by administrators.
However today we faced very weird issue that not able to access this hidden list. we can browse it under "/_catalogs/users" but we were not able to query it

Problem : 
 We were not able to query the User Information List in SharePoint 2013 On-Premise.
Tried following stuffs :
1. http://your-site-name/_vti_bin/ListData.svc/UserInformationList
2. http://your-site-name//_api/web/lists/GetByTitle(‘User Information List')
3. In PowerShell - 
$SITE = GET-SPSITE "HTTPS://your-site-name"
$WEB = $SITE.ROOTWEB

$LIST = $WEB.LISTS["USER INFORMATION LIST"]
All the above was giving the 404 error, List was not exist under the $web.Lists - which was very weird.
Solution:
After some r&d found the way to access it using SiteUserInfoList object

$web.SiteUserInfoList.Items
using this we can get the items of User information list.

Happy Coding!






Sunday, December 27, 2015

Office 365 People Search:Hide default result



Out of the box, People search result webpart shows the list of people as a result.

However, I had a requirement to remove the default people search coming in result webpart when page loads.

with the default query :
- {searchboxquery}contentclass=spspeople -accountname:rapidcircle.in


Spent few hours to find the solution finally got this useful tip from my Search guru MIKAEL SVENSON

To remove the default result need to bind the query with {?<query>}

the {?{searchboxquery}} will remove everything in the query if the {searchboxquery} variable is empty

Hope this small piece of information will save your time.





Tuesday, September 29, 2015

Permission denied for "Add and Customize Pages" in Sharepoint Online


Problem:

I have created root site in SharePoint Online, weird problem I started facing is being a site administrator I was not able to customize the page/ upload the any page or master page.

As in image below, for some reason I am set as Deny to "Add and Customize Pages".






The problem is related to 'Scripting Capabilities' being turned off by default for the root site collection in Office 365.

I found some article on community:

https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f?ui=en-US&=amp&rs=en-US&ad=US

It mentions that turning on scripting through the Sharepoint Admin Settings and wait around 24 hours. However, this did not work for me. still the issue was there.

Following lines of script did a trick.

Solution:

Connect-SPOService -Url https://<yoursitename>-admin.sharepoint.com -credential <admincredentials>

Set-SPOsite https://<your root site url> -DenyAddAndCustomizePages 0


And it immediately started working!

Happy Learning!

Monday, September 28, 2015

Display Template HTML file missing in SharePoint online

Today was trying to work on the display template in SharePoint Online.

As documented every where, that can go to Site Galleries->Master Page->Display Templates :     <your site url>/_catalogs/masterpage/Forms/AllItems.aspx and display templates folder, to get the existing templates.

However, I was just seeing all the .js files not the HTML where I supposed to work.

Finally found the cause is Publishing Feature.

Solution:

We must turn on SharePoint Server Publishing Infrastructure feature in the Site Collection Features. for the HTML pages to show up, from there we can edit the HTML files and create new display templates.

This is not documented any where.

Hope small tip will save your time!


Monday, May 11, 2015

Nintex 2013 Workflow Action: "Commit pending changes"


Commit pending changes is not well known action of Nintex, However very useful when you have scenario like I faced. 

I had a requirement like first to update the list item, and immediately need to take the count of updated items.

I spent quite amount of time to achieve this, First tried by doing simple workflow

1. Update list item

2. Query list (query the same list which updated)




Here after running this, started getting uncertain results. Sometime it works and fails.

Started applying workarounds to that

Tried to apply “Pause for” action in between, like below image.







Result was again uncertain, it pause was getting struck, was not resuming the workflow even after specified time.

I goggled and found many issues are reported for the same issue,

https://community.nintex.com/thread/2979

Third workaround was to add some loop which can add some pause before query, somehow even after safe loop, result was uncertain.


Finally found the impressive feature of Nintex, which has saved from all these hassles.

“Commit Pending Changes” – How exactly it is working ?

SharePoint workflow not starting the workload immediately, that means if I am adding update list item action, so its not sure that it will go and update the item at the same time, SharePoint doing batch job for it. As a result we are getting wrong result while querying it immediately.

As well other important thing to remember that The SharePoint workflow engine doesn't necessarily commit batched operations in the order they are displayed on the designer

Commit Pending Changes will wait/pause the workflow or say it waits until the workflow commits all other batch job started then after will move further.





This is very useful when you want workflow engine to follow the order you specified in workflow designer, or where the scenario where dependency on next to next actions.

Impressed with this Workflow action, Hope this piece of information will save your time!








Wednesday, April 29, 2015

Nintex 2013: Show display name in email notification for person field


In Nintex 2013, requirement was to show the user's "Display name" in email. looks a very simple issue by looking at the definition. However, to achieve this in workflow have to choose proper way to implement.
Two options are available to get this.

1. Using "Call web service" action
2. Using " Query user profile"


To move ahead with first step, we need to use Regular expression, web service and query xml  as described here in https://community.nintex.com/thread/1353 lots of configuration require to make this work. as well to store credentials of admin to call the web service, need to make the constant in Nintex management.

So In my implementation, I choose second option, fairly simple to use.

Steps to use "Query user profile" action to get display name:

1. Add "Query user Profile" from actions tab




 2. Double click to configure it, and select the person field , here selecting "Created By" field of the custom list




3. Expand the properties to retrieve, and select "Name" , click on Add



4. Select the variable name where you want to save query result, here using string variable named "CreatedByDisplayName"

5. Now, add the email notification action and from insert reference add the workflow variable.



6. That's all we are done.

No configuration/web service call/credentials require to store.

Hope this will help you!


Sunday, March 29, 2015

SharePoint 2013 List CRUD operation using Angular and REST API

This article explains how to perform the CRUD operation on a SharePoint List using Angular JavaScript and the REST API.

Create an empty SharePoint 2013 solution, In that add the module of Pages, JS & CSS.

In pages module add new item, Test.aspx.
In JS module, add new Javascript file with name Controller.js , CommonServices.js & SharePointListModule.js
Other Javascripts needs to add:
1. angular.min.js
2. anugular.min.js.map

Project structure will look something like below:





In SharePointListModule.js will create a App of angular, Paste the below code.

 //App creation  
 var app = angular.module("SharePointAngApp", [])  


For doing a CRUD operation with SharePoint List, will create an Angular Service to make a SharePoint REST API calls,



 app.service('testService', ['$http', '$q', function ($http, $q) {//common services}]);

This testService having dependency of $http & $q
Information related to $http & $q  https://docs.angularjs.org/api/ng/service/$http , https://docs.angularjs.org/api/ng/service/$q

Following code showing how to get, add, delete & update tasks from specified SharePoint list,

 //Common service for SharePoint CRUD operations  
 app.service('testService', ['$http', '$q', function ($http, $q) {  
   var formDigest = $('#__REQUESTDIGEST').val();  
   //Define the http headers  
   $http.defaults.headers.common.Accept = "application/json;odata=verbose";  
   $http.defaults.headers.post['Content-Type'] = 'application/json;odata=verbose';  
   $http.defaults.headers.post['X-Requested-With'] = 'XMLHttpRequest';  
   $http.defaults.headers.post['If-Match'] = "*";  
   $http.defaults.headers.post['X-RequestDigest'] = formDigest;  
   //Get the tasks  
   this.getTasks = function (listTitle) {  
     var dfd = $q.defer();  
     $http.defaults.headers.post['X-HTTP-Method'] = ""  
     var query = "?$select=Title,Status,Priority,ID,Body";  
     var restUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('" + listTitle + "')/items" + query;  
     $http.get(restUrl).success(function (data) {  
       dfd.resolve(data.d.results);  
     }).error(function (data) {  
       dfd.reject("error getting tasks");  
     });  
     return dfd.promise;  
   }  
   //Create a task , for now static text, we can pass input fields to add apropriate value   
   this.addTask = function (listTitle) {  
     var dfd = $q.defer();  
     $http.defaults.headers.post['X-HTTP-Method'] = "";  
     var restUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('" + listTitle + "')/items";  
     $http.post(restUrl, {  
       __metadata: {  
         type: "SP.List"  
       },  
       Title: "New Tasks"  
     }).success(function (data) {  
       //resolve the new data  
       dfd.resolve(data.d);  
     }).error(function (data) {  
       dfd.reject("failed to add task");  
     });  
     return dfd.promise;  
   }  
   //Update a task  
   this.updateTask = function (listTitle, task) {  
     var dfd = $q.defer();  
     $http.defaults.headers.post['X-HTTP-Method'] = "MERGE";  
     var restUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('" + listTitle + "')/items(" + task.ID + ")";  
     $http.post(restUrl, {  
       __metadata: {  
         type: "SP.List"  
       },  
       Title: task.Title  
     }).success(function (data) {  
       //resolve something  
       dfd.resolve(true);  
     }).error(function (data) {  
       dfd.reject("error updating task");  
     });  
     return dfd.promise;  
   }  
   //Delete a task  
   this.deleteTask = function (listTitle, task) {  
     var dfd = $q.defer();  
     $http.defaults.headers.post['X-HTTP-Method'] = "DELETE";  
     var restUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('" + listTitle + "')/items(" + task.ID + ")";  
     $http.post(restUrl)  
       .success(function (data) {  
         //resolve something  
         dfd.resolve(true);  
       }).error(function (data) {  
         dfd.reject("error deleting task");  
       });  
     return dfd.promise;  
   }  
 }]);  

Now, its time to consume all these services by creating an angular controller.
 app.controller('spTasksController', function ($scope, $http, testService) {  
 //functions to consume the service methods});  

Full code to consume the above service methods is below, paste it in Controller.js
 //Controller to featch data from Tasks list  
 app.controller('spTasksController', function ($scope, $http, testService) {  
   $scope.editing = false;  
   //example populating tasks  
   testService.getTasks("Workflow Tasks").then(function (result) {  
     $scope.tasks = result;  
   });  
   //example to add task  
   $scope.addTask = function (listName) {  
     testService.addTask(listName).then(function (result) {  
       //update the scope with new task created  
       $scope.tasks.push(result);  
       $scope.task = {};  
       $scope.$apply();  
     });  
   };  
   // example to update task  
   $scope.updateTask = function (listName, item) {  
     testService.updateTask(listName, item).then(function (result) {  
       $scope.editing = $scope.tasks.indexOf(item);  
       $scope.$apply();  
     });  
   };  
   $scope.editItem = function (index) {  
     $scope.editing = $scope.tasks.indexOf(index);  
   }  
   $scope.saveField = function (index) {  
     if ($scope.editing !== false) {  
       $scope.editing = false;  
     }  
   };  
   // example to delete task  
   $scope.deleteTask = function (listName, item,index) {  
     testService.deleteTask(listName, item).then(function (result) {  
       $scope.tasks.splice(index, 1);  
       $scope.$apply();  
     });  
   };  
 });  

Now, its time to bind the view. add the below piece of code in test.aspx
 <%@ Page Language="C#" MasterPageFile="~masterurl/default.master" Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=15.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>  
 <%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>  
 <%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>  
 <%@ Import Namespace="Microsoft.SharePoint" %>  
 <%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>  
 <%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>  
 <asp:Content ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">  
   <SharePoint:EncodedLiteral runat="server" Text="<%$Resources:wss,multipages_homelink_text%>" EncodeMethod="HtmlEncode" />  
   -   
      <SharePoint:ProjectProperty Property="Title" runat="server" />  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderPageImage" runat="server">  
   <img src="/_layouts/15/images/blank.gif?rev=23" width='1' height='1' alt="" />  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">  
   <label class="ms-hidden">  
     <SharePoint:ProjectProperty Property="Title" runat="server" />  
   </label>  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderTitleAreaClass" runat="server">  
   <SharePoint:UIVersionedContent runat="server" UIVersion="<=3">  
     <contenttemplate>  
           <style type="text/css">  
             td.ms-titleareaframe, .ms-pagetitleareaframe {  
               height: 10px;  
             }  
             div.ms-titleareaframe {  
               height: 100%;  
             }  
             .ms-pagetitleareaframe table {  
               background: none;  
               height: 10px;  
             }  
             .ms-viewheadertr ms-vhltr {  
               background: rgb(246,248,249); /* Old browsers */  
               /* IE9 SVG, needs conditional override of 'filter' to 'none' */  
               background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Y2ZjhmOSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iI2U1ZWJlZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUxJSIgc3RvcC1jb2xvcj0iI2Q3ZGVlMyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmNWY3ZjkiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);  
               background: -moz-linear-gradient(top, rgba(246,248,249,1) 0%, rgba(229,235,238,1) 50%, rgba(215,222,227,1) 51%, rgba(245,247,249,1) 100%); /* FF3.6+ */  
               background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(246,248,249,1)), color-stop(50%,rgba(229,235,238,1)), color-stop(51%,rgba(215,222,227,1)), color-stop(100%,rgba(245,247,249,1))); /* Chrome,Safari4+ */  
               background: -webkit-linear-gradient(top, rgba(246,248,249,1) 0%,rgba(229,235,238,1) 50%,rgba(215,222,227,1) 51%,rgba(245,247,249,1) 100%); /* Chrome10+,Safari5.1+ */  
               background: -o-linear-gradient(top, rgba(246,248,249,1) 0%,rgba(229,235,238,1) 50%,rgba(215,222,227,1) 51%,rgba(245,247,249,1) 100%); /* Opera 11.10+ */  
               background: -ms-linear-gradient(top, rgba(246,248,249,1) 0%,rgba(229,235,238,1) 50%,rgba(215,222,227,1) 51%,rgba(245,247,249,1) 100%); /* IE10+ */  
               background: linear-gradient(to bottom, rgba(246,248,249,1) 0%,rgba(229,235,238,1) 50%,rgba(215,222,227,1) 51%,rgba(245,247,249,1) 100%); /* W3C */  
               filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f6f8f9', endColorstr='#f5f7f9',GradientType=0 ); /* IE6-8 */  
             }  
             .ms-vh2 {  
               font-size: 13px;  
               font-weight: 500;  
             }  
           </style> <!--[if gte IE 9]>  
  <style type="text/css">  
   .gradient {  
     filter: none;  
   }  
  </style> <![endif]-->  
     </contenttemplate>  
   </SharePoint:UIVersionedContent>  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">  
   <meta name="CollaborationServer" content="SharePoint Team Web Site" />  
   <SharePoint:StyleBlock runat="server">  
     .s4-nothome {  
      display:none;  
 }  
   </SharePoint:StyleBlock>  
   <script type="text/javascript" src="../JS/jquery.min.js">//<![CDATA[//]]></script>  
   <script type="text/javascript" src="../JS/angular.min.js"></script>  
   <script type="text/javascript" src="../JS/SharePointListModule.js"></script>  
   <script type="text/javascript" src="../JS/Controllers.js"></script>  
   <script type="text/javascript" src="../JS/Common.Services.js"></script>  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderSearchArea" runat="server">  
   <SharePoint:DelegateControl runat="server"  
     ControlId="SmallSearchInputBox" />  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderLeftActions" runat="server" />  
 <asp:Content ContentPlaceHolderID="PlaceHolderLeftNavBar" runat="server">  
   <div class="page">  
     <div class="row">  
       <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">  
         <div class="navHolder" id="fisCustomNav">  
           <ul class="nav-site" id="fisNavroot">  
           </ul>  
           <ul class="nav-device-main">  
             <li></li>  
           </ul>  
         </div>  
       </div>  
     </div>  
   </div>  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderPageDescription" runat="server">  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderBodyAreaClass" runat="server">  
   <SharePoint:StyleBlock runat="server">  
     .ms-bodyareaframe {  
      padding: 0px;  
 }  
   </SharePoint:StyleBlock>  
 </asp:Content>  
 <asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">  
   <form class="form-horizontal" name="taskForm" novalidate>  
     <div class="page" ng-app="SharePointAngApp">  
       <div class="row">  
         <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 replacement-btn-list">  
           <h3 class="text-bold text-color-dark ">List of Tasks:</h3>  
         </div>  
       </div>  
       <div class="row">  
         <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">  
           <div class="table-responsive replacement-list" ng-controller="spTasksController">  
             <table class="table">  
               <tr>  
                 <th>Tasks</th>  
                 <th>Status</th>  
                 <th>Prority</th>  
                 <th>Update</th>  
               </tr>  
               <tbody>  
                 <tr ng-repeat="task in tasks">  
                   <td><span ng-hide="editMode">{{task.Title}}</span><input type="text" ng-show="editMode" ng-model="task.Title" /></td>  
                   <td>  
                     <div>{{task.Status}}</div>  
                   </td>  
                   <td>  
                     <div>{{task.Priority}}</div>  
                   </td>  
                   <td><span>  
                     <input type="button" ng-hide="editMode" ng-click="editMode = true; editItem(task) " value="Edit" /></span>  
                     <span>  
                       <input type="button" ng-show="editMode" ng-click="editMode = false; updateTask('Workflow Tasks',task)" value="Save"></span></td>  
                   <td>  
                     <div>  
                       <input type="button" ng-click="deleteTask('Workflow Tasks',task,$index)" value="Delete" /></div>  
                   </td>  
                 </tr>  
                 <tr>  
                   <td>  
                     <input type="button" ng-click="addTask('Workflow Tasks')" value="Add" /></td>  
                 </tr>  
               </tbody>  
             </table>  
           </div>  
         </div>  
       </div>  
     </div>  
   </form>  
 </asp:Content>  


If you noticed in parent div i have added :  <div class="page" ng-app="SharePointAngApp">
and in child div added ng-controller

  <div class="table-responsive replacement-list" ng-controller="spTasksController">

Once we add the controller, we can access all the scope variables in child elements.

Output will looks like below:




Hope this will help you to build SharePoint applications using Angualr.
Happy learning!