Saturday, December 17, 2016

How to get more items than threshold limit from SharePoint 2013 using REST API

While working with SharePoint, we always get stuck with the condition of not able to fetch more than threshold items(which is 5000 items per query). I have found a very easier way to get all item from a list into memory and the use it for binding, comparing or any other purpose, we want to use it for. Here my approach would be keep querying list till the time I am getting count equal to current threshold value.

 var fields = []; //All fields internal names
var expand = []; //Fields name to expand
var listName = string; // List title goes here
var listTemplate = []; // All records goes here, needs to be global
var ItemIDs = []; //An aarey to store Ids, needs to be global
function loadListData() {
   var ID = 0;
if(ItemIDs.length > 0)
    ID = ItemIDs[ItemIDs.length - 1];
$http({
method: "GET",
url: siteUrl + "/_api/web/lists/getByTitle('" + listName + "')/items()/"
+ "?$select=ID, " + fields
+ "&$expand=" + expand
+ "&$filter=ID gt "+ ID
+ "&$orderby=ID asc"
+ "&$top=5000", // SP limit is 100 by default
headers: { "Accept": "application/json;odata=verbose" }
}).success(function(data, status, headers, config) {
listTemplate = listTemplate.concat(data.d.results);
ItemIDs = ItemIDs.concat(data.d.results.map(function(v){ return (v.ID); }));
if(data.d.results.length == 5000) {
loadListData();
}
else {
   alert("Total records got from list are: " + ItemIDs.length);
//Next Function you want to execute after getting all records from list.
}
}).error(function (data, status, headers, config) {
 alert("Error while fetching data from list: "+ listName):
});
};

Just include this function with keeping these variables in global scope. Please let me know for any assistance or issues you face around it.

Friday, December 16, 2016

Get details of each user in SharePoint Site using ECMA Script

As you all are aware how tedious its to add find out details of each user into a SharePoint site.
There are only two ways for it first one is going to check permission option in site collection and then checking for each user one by one. Another option would be to get into each group ad then search for them.
To get rid of such time consuming process for users having no access to server, I have created a small piece of code script for getting details for all users using Client Side coding:

<html>
<head>
<script src="//code.jquery.com/jquery-1.12.3.js" type="text/javascript"></script>
<script src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css" /> 
<script type="text/javascript">
$(document).ready(function() {
    
} );
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', GetAllUsersInCurrentSite);
var groupUserMapping = [];
var AllUsersInSite = [];

    function GetAllUsersInCurrentSite() {
alert("Hi..");
var clientContext  = SP.ClientContext.get_current();
collGroup = clientContext.get_web().get_siteGroups();
clientContext.load(collGroup);
clientContext.load(collGroup, 'Include(Users)');
clientContext.executeQueryAsync(onQuerySucceeded,function() {});
}

function onQuerySucceeded(sender, args) {
var userInfo = '', groupInfo = '';
var groupEnumerator = collGroup.getEnumerator();
while (groupEnumerator.moveNext()) {
var oGroup = groupEnumerator.get_current();
var collUser = oGroup.get_users();
var userEnumerator = collUser.getEnumerator();
while (userEnumerator.moveNext()) {
  var oUser = userEnumerator.get_current();
  AllUsersInSite.push(oUser.get_id());
  groupUserMapping.push({GroupTitle: oGroup.get_title(), UserLoginName: oUser.get_loginName(), UserId: oUser.get_id(), UserTitle: oUser.get_title()});
}
}
var AllUniqueUserIds = AllUsersInSite.unique();
var Contacts = [];
AllUniqueUserIds.forEach(function(x){
var y =[];
var UserTitle = LoginName = '';
y["ID"] = x;
var groupVal = groupcount = '';
var groupMatch = groupUserMapping.filter(function (el) {
 return el.UserId == x;
});
if(groupMatch && groupMatch.length > 0) {
groupMatch.forEach(function(val){
groupVal += val.GroupTitle +", ";
UserTitle = val.UserTitle;
LoginName = val.UserLoginName;
groupcount = groupMatch.length;
});
}
y['UserName'] = UserTitle;
y['LoginName'] = LoginName;
y['GroupDetails'] = groupVal;
y['GroupCount'] = groupcount; 
Contacts.push(y);
});

$('#example').DataTable( {
        data: Contacts,
        columns: [
            { data: "UserName", title: "User Name"  },
            { data: "LoginName", title: "Login Name" },
            { data: "GroupDetails", title: "Group Names" },
{ data: "GroupCount", title: "Group Count" }
        ]
} );

}

Array.prototype.contains = function(v) {
    for(var i = 0; i < this.length; i++) {
        if(this[i] === v) return true;
    }
    return false;
};

Array.prototype.unique = function() {
    var arr = [];
    for(var i = 0; i < this.length; i++) {
        if(!arr.contains(this[i])) {
            arr.push(this[i]);
        }
    }
    return arr; 
}
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun /*, thisp*/) {
    var len = this.length >>> 0;
    if (typeof fun != "function")
    throw new TypeError();

    var res = [];
    var thisp = arguments[1];
    for (var i = 0; i < len; i++) {
      if (i in this) {
        var val = this[i]; // in case fun mutates this
        if (fun.call(thisp, val, i, this))
        res.push(val);
      }
    }
    return res;
  };
}
</script>
</head>
<body>
<table id="example" class="display" width="100%"></table>
</body>
</html>

Just need to make sure that you have access to internet as I am referring JQuery and Data table CDN. As soon you add this HTML file into a SharePoint Page CEWP. You would be able to see the details of each user, group names- user is part of, group count as below screenshot.



I have filtered it my name just to show my own membership with few generic groups. :)

Also just add reference to table export library and code to export this table into excel:

$("table[id='example']").tableExport({
headings: true,                    // (Boolean), display table headings (th/td elements) in the <thead>
footers: true,                     // (Boolean), display table footers (th/td elements) in the <tfoot>
formats: ["xlsx"],    // (String[]), filetypes for the export
fileName: "id",                    // (id, String), filename for the downloaded file
bootstrap: true,                   // (Boolean), style buttons using bootstrap
position: "bottom" ,                // (top, bottom), position of the caption element relative to table
ignoreRows: null,                  // (Number, Number[]), row indices to exclude from the exported file
ignoreCols: null ,                  // (Number, Number[]), column indices to exclude from the exported file
ignoreCSS: ".tableexport-ignore"   // (selector, selector[]), selector(s) to exclude from the exported file
});

We can also extend above functions to get details regarding users. Hope it helps someone.!



Tuesday, December 22, 2015

Easiest way to add Auto complete lookup to SharePoint 2013 or Online Form Fields using SPServices.

Today we will see how can we add an auto complete function to a SharePoint field in SharePoint 2013 or Online using SPServices. It will provide an easier way to provide user lookup functionality as a help instead of mandatory way and also add below code to on NewForm.aspx of your destination List by adding a Script Editor Webpart:

<script language="javascript" src="//code.jquery.com/jquery-1.6.2.min.js"
type="text/javascript"></script>
<script language="javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.02/jquery.SPServices-2014.02.min.js"
type="text/javascript"></script>
<script type="text/javascript">
    $(document).ready(function () {
        $().SPServices.SPAutocomplete({
            sourceList: "SourceList", // Source List Name
            sourceColumn: "Title", // Source List Column from where you want to fetch it.
            columnName: "DestinationListColumn", // Destination List Column where you wan to add it.
            ignoreCase: true,
            numChars: 2,
            slideDownSpeed: 'fast'
        });
    });</script>

Wednesday, August 19, 2015

Best way to validate a SharePoint Enhanced Rich text field using JQuery.

Recently I got a requirement to create a new Enhanced Rich text field in my existing application. We also need to validate that its not blank. So at first I know, it will be bit complex due to any obvious selector unavailability for Enhanced Rich Text Field in SharePoint. I searched a lot and found no. of suggestion but nothing seems very logical to me and also did not work for me. Then I have written below script to deal with it.

//Rich Text Field Validation Code

var RichtTxtContent = $('nobr:contains("Rich Text Field Title")').closest('tr').find('div[id$="TextField_inplacerte"]').text();
if( RichtTxtContent.length === 1 ||  RichtTxtContent.length === 0)
{
alert("Please enter  Richt Text Content .");
SetRemoveStyleOnFocus($('nobr:contains("Rich Text Field Title")').closest('tr').find('div[id$="TextField_inplacerte"]'));
return false;

I have just selected whole Row with the Rich Text Field Title, then found out a div with the text its always append it into this div client Id. Refer below screen shot to see the ID.


Then we need to check length as by default its set to 1. We need to compare with both 1 and 0.


Here is a function which will make sure that field is highlighted with focus whenever its blank. You can write your own classes to highlight it.

function SetRemoveStyleOnFocus(inputField)
{
$(inputField).focus(function() {
    $(this).addClass("focus");
});
$(inputField).blur(function() {
    $(this).removeClass("focus");
    });

    $(inputField).focus();
}

We just need to call this piece of code on Pre Save event of a SharePoint OOB New\Edit form or any other event as per need.


Validate no. of characters and their type entered into any input field using Java Script.

Here are I am writing an example how can we make sure that entered input in a filed should be only a 6 Digit number. You can modify it to according to your requirements:

var InputFieldValue = $("#InputFiledID").val();//Bit Jquery :) You can use java script too.

if( InputFieldValue!= '')
    {
var digits = "0123456789";
        var temp;
        if (InputFieldValue.length > 6){
        alert("Entered value cannot be more than 6 Digit Numbers ");
        return false;
        }
        else if(InputFieldValue.length < 6){
        alert("Entered value cannot be less than 6 Digit Numbers ");
        return false;
        }
        else {
for (var i = 0; i < InputFieldValue.length ; i++) {
        temp = InputFieldValue.substring(i, i + 1);
        if (digits.indexOf(temp) == -1) {
        alert("Entered value cannot contain any Character.");
        return false;
        }
        }
        }
     }

On the above code we have provided a string of characters which we want to permit as inputs. In this case we want to validate against numerals.  Then we select each character of input field value and try to find it into our format string, if it does not contain we return vale as false.

This can be extend for checking against any format.

Here is an example how can we extend it. For an input in this format "xx-xx-xx" where 'x' needs to be a number:

if( InputFieldValue != '')
{
var digits = "0123456789";
        var temp;
        if (InputFieldValue.length > 8){
        alert("Entered Value cannot be more than 6 Digit Numbers ");
                return false;
                }
                else if(InputFieldValue.length < 8)
                {
                alert("Entered Value cannot be less than 6 Digit Numbers ");
                return false;
                }
                else {
for (var i = 0; i < InputFieldValue.length; i++) {
            temp =InputFieldValue.substring(i, i + 1);
            if(i==2|| i==5)
            {
            if(temp!='-')
            {
            alert("Entered Value be in xx-xx-xx format. Does not conation - in current values.");
                   return false;
                   }
            }
            else if (digits.indexOf(temp) == -1) {
                alert("Entered Value cannot contain any Character ");
                return false;
            }
            }
             }
            }

On the above code, we are making sure that on 3rd and 5th position its always contain '-'.

Hope it helps someone.


Thursday, July 16, 2015

Creating multiple layer of folders in SharePoint using SPServices and Jquery on any version.

Recently I got a requirement, where user wants to create a set of multiple layer of multiple folders. Creating all those folders one by one with same structure manually is very time consuming work. At the same time we wanted not to deploy any custom C# solution and we need it on SP2007. So I decided to use SPServices.

Below is the code which I have added into a Content Editor Webpart:

<script language="javascript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="http://www.sympraxisconsulting.com/Demos/jQuery%20Libraries/jquery.SPServices-0.6.0.min.js"></script>
<script language="javascript" type="text/javascript">
  $(document).ready(function() {
  });

function CallOnButtonClick()
{
var librarynaem = "List or Library Title";
var newfolder = document.getElementById("topFolderName").value;
CreateFolder(librarynaem, newfolder);
}  

function CreateFolder(librarynaem, newfolder) {
 jQuery().SPServices({
  operation: "UpdateListItems",
  async: false,
  listName: librarynaem ,
  updates: "<Batch OnError='Continue' PreCalc='TRUE' ListVersion='0' >" +
    "<Method ID='1' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder + "</Field>" +
    "</Method>" +
    "<Method ID='2' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder1"+ "</Field>" +
    "</Method>" +
"<Method ID='3' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder1/Folder11"+ "</Field>" +
    "</Method>" +
"<Method ID='4' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder1/Folder12"+ "</Field>" +
    "</Method>" +
"<Method ID='5' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder2"+ "</Field>" +
    "</Method>" +
"<Method ID='6' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder3"+ "</Field>" +
    "</Method>" +
"<Method ID='7' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder3/Folder31"+ "</Field>" +
    "</Method>" +
"<Method ID='8' Cmd='New'>" +
     "<Field Name='FSObjType'>1</Field>" +
     "<Field Name='BaseName'>" + newfolder +"/Folder3/Folder32"+ "</Field>" +
    "</Method>" +
   "</Batch>",
  completefunc: function(xData, Status) {
   //alert("Status=" + Status); 
alert("New Service Added. Please refresh.");
 document.getElementById("topFolderName").value = "";
   }
 });
 }
  
</script>
<input type="text" id="topFolderName" />
<input type="button" onclick="CallOnButtonClick()" value="Add New Folder Structure">

Running this code will create folders and sub folders. We can create it any level using "/" for a new level in name parameter.

Friday, July 10, 2015

Find out users from a particular domain and remove them from whole SharePoint web application with checking status in Active Directory.

Recently we have got an requirement to remove users from a particular domain from SharePoint Web-application. In addition to it we also need to check if that user is still active on that Active Directory.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.IO;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace CheckCurrentUserStatus
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("Started...");

            Console.WriteLine("Please enter any site collection URL to access Web Application and press enter");
            string siteURL = Convert.ToString(Console.ReadLine());

            Console.WriteLine("Please enter domain name and press enter.");
            string DomainName = Convert.ToString(Console.ReadLine());
            string StartedTime = DateTime.Now.ToString();

            Console.WriteLine("Started For... Site URL " + siteURL + " and Doamin Name " + DomainName);
            GetAllUsersFormADomain(siteURL, DomainName);
            Console.WriteLine("Stared Executing at : " + StartedTime);
            Console.WriteLine("Complted Executing at : " + System.DateTime.Now.ToString());
            Console.Read();
        }

        private static bool DoesUserExistsDisabledAndDeletable(string strDomain, string strUserName)
         {
                 bool isUserExistsAndDisabled = false; 
                    using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, strDomain))
                    {
                       isUserExistsAndDisabled =  ForEachUserNameCheckExistsAndDisabled(strDomain, strUserName, pc);
                    }
                 return isUserExistsAndDisabled;

         }

        private static bool ForEachUserNameCheckExistsAndDisabled(string strDomain, string strUserName, PrincipalContext pc)
        {
            bool isUserExistsAndDisabled = false;
            
            UserPrincipal up = UserPrincipal.FindByIdentity(pc, strUserName);

            bool UserExists = (up != null);
            if (UserExists)
            {
                bool isEnabled = (bool)up.Enabled;
                if (isEnabled)
                {
                    isUserExistsAndDisabled = false;
                    Console.WriteLine(strDomain + " " + strUserName + " " + "Account Enabled...!!!");
                    File.AppendAllText("UsersStatus.txt", strDomain + " " + strUserName + " " + "Account Enabled...!!! Can not be Deleted." + "\r\n");
                    //TextWriter tsw = new StreamWriter(@"UsersStatusEnabled.txt", true);
                }
                else
                {
                    isUserExistsAndDisabled = true;
                    Console.WriteLine(strDomain + " " + strUserName + " " + "Account is Disabled...!!!");
                    File.AppendAllText("UsersStatus.txt", strDomain + " " + strUserName + " " + "Account is Disabled...!!! Deleted from SharePoint" + "\r\n");
                                 
                    //TextWriter tsw = new StreamWriter(@"UsersStatusDisabled.txt", true);
                }
            }
            else
            {
                isUserExistsAndDisabled = true;
                Console.WriteLine(strDomain + " " + strUserName + " " + "Account does not exists...!!!");

                File.AppendAllText("UsersStatus.txt", strDomain + " " + strUserName + " " + "Account does not exists...!!! Deleted from SharePoint." + "\r\n");
                //TextWriter tsw = new StreamWriter(@"UsersNotFound.txt", true);
            }
            return isUserExistsAndDisabled;
        }

        private static void GetAllUsersFormADomain(string siteURL, string DomainName)
        {
           
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite objSPSite = new SPSite(siteURL))
                {
                    if (objSPSite!=null)
                    {
                        DeleteUsers(objSPSite, DomainName);
                    }
                }
                
            });
        }

        private static void DeleteUsers(SPSite objSPSite, string DomainName)
        {
            SPWebApplication objSPWebApp = objSPSite.WebApplication;
                    foreach (SPSite siteCollection in objSPWebApp.Sites)
                    {
                        foreach (SPWeb web in siteCollection.AllWebs)
                        {
                            SPUserCollection objSPColl = web.AllUsers;
                            foreach (SPUser user in web.AllUsers)
                            {
                                string[] DomainAndUserName = user.LoginName.ToString().Split('\\');
                                if (DomainAndUserName != null && DomainAndUserName.Length > 0)
                                {
                                    string Domain = DomainAndUserName[0];
                                    string UserName = DomainAndUserName[1];
                                    if (Domain.ToLower().Equals(DomainName.ToLower()))
                                    {
                                        if (DoesUserExistsDisabledAndDeletable(DomainName.ToLower(), user.LoginName))
                                        {
                                            web.SiteUsers.Remove(user.LoginName);
                                        }
                                    }
                                }

                            }
                        }
                   }
        }

        
    }
}