Friday, 22 January 2016

How to upgrade SharePoint from one version to another version without any downtime

Hi All,


Today I want to share about how to upgrade SharePoint farm from one version to another version without downtime. This is the best approach when we are working on business application where we can not take down time or we can say where will have impact on business if we take down time like banking applications.


Here we need to set up Disaster Recovery farm has been set up with SQL Server AlwaysOn.


Now follow the below steps to upgrade SharePoint farm.


Step 1: Leave users going to primary site and meanwhile patch disaster-recovery/secondary site.
    1. Ddetach the content database from the DR farm 1st, as PSConfig will fail to finish the upgrade while these databases are still connected to the farm & in read-only mode (as the DR site will still be the secondary replica in SQL Server).
    2. syntax: Dismount-SPContentDatabase <ContentDB GUID>
    3. PSConfig will update all databases except the content databases.
    4. Re-attach the content databases, and maybe even run an incremental crawl if you want your indexes to include updates since the content DBs went offline.
    5. Syntax: Mount-SPContentDatabase <databasename> -DatabaseServer <db_server> -WebApplication <webapplication_URL>
       
    6. Verify SharePoint is happily working again on the DR site – check logs, site-access etc


Step 2: Switch users to the upgraded DR site + failover SQL Server to use the secondary node as the new primary. SharePoint on the DR site will use the content-databases in compatibility mode but with read/write access.
There will be a service interruption while this simultaneous databases + user switchover happens. Might want to make the switch at night, for example.

Step 3: Patch primary farm, now that users are on the DR site.
Once the normal farm is verified as healthy again, failover users there again if you so wish.
Both farms are now upgraded with content-databases in compatibility mode.

Step 4: On the farm with read/write access to the content-databases, finish the upgrade with Upgrade-SPContentDatabase. This may cause some read-only access while the upgrade is happening but it’ll be much less read-only time than the safer method below.
This is the preferred way: read/write functionality still works for users almost without interruption. This full functionality is available much more than was previously possible as AlwaysOn lets us switch primary servers back & forth trivially.


Shortly: upgrade DR farm 1st (with content DBs disconnected) while users still use the primary farm. Then reconnect content DBs, switch users + content DBs at the same time to DR farm once the DR farm is upgraded. Once everyone’s on the DR farm, upgrade primary farm. Finally upgrade content-databases in PowerShell.


Reference

Tuesday, 19 January 2016

How to retrieve all items from list (having more than 5000) using CSOM?

Hi All,


As we know that by default the Threshold limit to retrieve litem from a list is 5,000 and OneDrive for Business has a 20,000 limit.
So any list with more then 5,000 items can cause some problems in your apps.


Luckily CSOM allows you to retrieve all items by using the ListItemCollectionPostion. Every time you execute a query for list items, you will be presented with a ListItemCollection that contains the ListItemCollectionPosition.


If that ListItemCollectionPosition is not null you can use that position to execute the same query again, however with a different starting point. This way you can ‘loop’ through all items in a list and construct an object that contains all your items. By putting everything in a while loop you are making sure that you will retrieve all items.



string siteUrl = "http://MyServer/sites/MySiteCollection";
ClientContext clientContext = new ClientContext(siteUrl);
Web site = clientContext.Web;
List targetList = site.Lists.GetByTitle("Announcements");
CamlQuery query = new CamlQuery();
query.ViewXml = "<View><ViewFields><FieldRef Name='Title'/></ViewFields><RowLimit>10 </RowLimit></View>";
ListItemCollection collListItem = targetList.GetItems(query);
clientContext.Load(collListItem);
clientContext.ExecuteQuery();
string msg = "Titles, 10 at a time:\n";
foreach (ListItem myListItem in collListItem)
msg += "\nTitle=" + myListItem["Title"];
Console.WriteLine(msg);
ListItemCollectionPosition position = collListItem.ListItemCollectionPosition;
do
{
msg = "";
query.ListItemCollectionPosition = position;
collListItem = targetList.GetItems(query);
clientContext.Load(collListItem);
clientContext.ExecuteQuery();
position = collListItem.ListItemCollectionPosition;
foreach (ListItem myListItem in collListItem)
msg += "\nTitle=" + myListItem["Title"];
Console.WriteLine(msg);
} while (position != null);




Monday, 18 January 2016

Limitations with RunWithElevatedPrivelege in SharePoint

Hi all,


I just want to share couple of things about security issues with RunWithElevatedPriveleges in SharePoint.


Elevated Privilege can be used to bypass or work with security, and can be performed either through SPSecurity or through impersonation techniques involving the SPUserToken and the SPSite class.


Avoid using SPSecurity.RunwithElevatedPrivilege to access the SharePoint object model. Instead, use the SPUserToken to impersonate with SPSite. If you do use SPSecurity.RunwithElevatedPrivilege, dispose of all objects in the delegate. Do not pass SharePoint objects out of the RunwithElevatedPrivilege method.


Only use SPSecurity.RunwithElevatedPrivilege to make network calls under the application pool identity. Don't use it for elevation of privilege of SharePoint objects.


Here the sample code to show what is the issue with RunWithElevatedPrivileges.


SPList taskList=null;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
      SPSite elevatedSite = SPContext.Current.Site;

      using (SPWeb elevatedWeb = elevatedSite.OpenWeb())
      {
           taskList = elevatedWeb.Lists["Tasks"]
      }
   }
});
//This code will succeed even outside the block as it is accessed via elevated SPWeb. Hence Security Risk.
taskList.Delete();



Always use the SPSite constructor with an SPUserToken to create an elevated privilege security context in SharePoint.


This is most recommend way and best practice  to perform impersonation in context of SharePoint. However,When using SPUserToken, you need ensure that the user exists whom you are impersonating, and that user has the proper permissions. In production scenarios, you may not know the user in advance and this technique may not work
Below is the example to impersonate SHAREPOINT\SYSTEM account.
SPWeb oWeb = SPContext.Current.Web;
SPUserToken token = oWeb.AllUsers[@"SHAREPOINT\SYSTEM"].UserToken;
using (SPSite elevatedSite = new SPSite(oWeb.Site.ID, token))
 {
    using (SPWeb elevatedweb = site.OpenWeb())
     {
      // Perform administrative actions by using the elevated site and web objects.
      // elevatedWeb.CurrentUser.LoginName gives SHAREPOINT\system
      // WindowsIdentity.GetCurrent().Name gives current logged-in username i.e. SPContext.Current.Web.CurrentUser.LoginName.
      // Hence,Only SharePoint Security context is changed while Windows Security context is not changed.
     }
 }



If you see the code above, WindowsIdentity.GetCurrent().Name is same as the Name of current user making the request which is SPContext.Current.Web.CurrentUser.


So  any call to external systems like DB or WebServices will be made by windows account of the current user. It succeeds or not depends on the permissions that the user have on that external system.


If you want make network calls under the application pool identity  or you don’t have a valid and known SPUser to retrieve SPUsertoken then SPSecurity.RunWithElevatedPrivileges is the only choice.



And also the tokens time out after 24 hours, so they can be used in the code that needs to impersonate users in the case of workflow actions or asynchronous event receivers occurring within 24 hours.After the  SPUserToken object is returned to the caller, it is the caller’s responsibility to not use the token after it is expired.
The token timeout value can be set by using the Windows PowerShell console or stsadm.
   
stsadm -o setproperty -propertyname token-timeout -propertyvalue 720

Sunday, 22 November 2015

CURD operations on list using RESTAPI and AngularJS in SharePoint 2013 - Part2

Hi, in my previous post we have seen how to get SharePoint list data and show in tabular format using REST API with AngularJS.

Now i want to show to create new form and add new list item to list using REST API and AngularJS.


Here i will add one button under list item details and name it as "Create New" and it's look like as below.




Now when we click on the button i will display a new form to insert new item.

It looks like as below.



In this new form, we have single line of text, multiline of text, date picker, hyperlink, true/false(checkbox), dropdown and people picker.

This example, i have covered most of the column types.

For people picker am using Clientpeoplepicker.

In this example, am using dropdown column and dropdown values are loaded while page loading as below script.

// Loading dropdown default values
$scope.items = [
    { id: 1, name: 'Action'},
    { id: 2, name: 'Drama'}];

And dropdown field design is


<div class="form-group">

  <label class="col-sm-2 control-label">Book type: </label>
  <div class="col-sm-10">
   <select ng-model="selectedItem"
          ng-options="item as item.name for item in items">
  </select>
 </div>
</div>

For date picker, am using third party control for better look i.e. KENDO

and date picker field look as below




First we need to include all the required js and css files

<link rel="stylesheet" href="<sitename>/SiteAssets/Kendo/kendo.common-material.min.css" />
<link rel="stylesheet" href="<sitename>/SiteAssets/Kendo/kendo.material.min.css" />
 <script src="<sitename>/SiteAssets/Kendo/jquery.min.js"></script>
 <script src="<sitename>/SiteAssets/Kendo/angular.min.js"></script>
 <script src="<sitename>/SiteAssets/Kendo/kendo.all.min.js"></script>

Now load kendo module as below

var myAngApp = angular.module('bookapp', ["kendo.directives"]);

For, people picker am using SPClientpeoplepicker.

For this, we need to load the below script.

$(document).ready(function () {

    // Specify the unique ID of the DOM element where the

    // picker will render.
     initializePeoplePicker('peoplePickerDiv');

});

function initializePeoplePicker(peoplePickerElementId) {


    // Create a schema to store picker properties, and set the properties.

    var schema = {};
    schema['PrincipalAccountType'] = 'User,DL,SecGroup,SPGroup';
    schema['SearchPrincipalSource'] = 15;
    schema['ResolvePrincipalSource'] = 15;
    schema['AllowMultipleValues'] = false;
    schema['MaximumEntitySuggestions'] = 50;
    schema['Width'] = '280px';

    // Render and initialize the picker.

    // Pass the ID of the DOM element that contains the picker, an array of initial
    // PickerEntity objects to set the picker value, and a schema that defines
    // picker properties.
    SPClientPeoplePicker_InitStandaloneControlWrapper(peoplePickerElementId, null, schema);
}

People picker looks like as below and when users start typing can see search results as below.



Now when user enter all the details and click on add button all these details need to add to list and refresh the page with added item.

now we need to update control as below,

###############################################

$scope.Add = function() {
$scope.Booktype=$scope.selectedItem.name;
if($scope.HasPassport) 
{
$scope.isread=true;
}
else
{
$scope.isread=false;
}
    var d = new Date($scope.dateString);
    $scope.Bookreleasedate= d.toISOString();
    var userlanid="'"+$.parseJSON($('#peoplePickerDiv_TopSpan_HiddenInput').val())[0].Key+"'";
    var name=userlanid.replace(':', '%3A').replace('|', '%7C').replace('#', '%23').replace(/'/g, '%27');
var promisedata=bookService.getuserid(name,$scope);
promisedata.then(function(data) {
bookService.addBook('Bookshelf',$scope);
console.log('This ANOTHER success method was assigned AFTER calling to async()', data);
}, function(error) {
console.log('This ANOTHER failure method was assigned AFTER calling to async()', error);
});
    }

update service as below where actual item adding to list.

###############################################################

 //Create a book
this.addBook = function (listTitle,$scope) {
        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.Data." + listTitle + "ListItem"
       },
       Title: $scope.BookTitle,
Booksummary: $scope.BookSummary,
Bookisread: $scope.isread,
ReleaseDate: $scope.Bookreleasedate,
Bookreleasedate:$scope.Bookreleasedate,
EmpId: $scope.emp,
Booktype:$scope.Booktype,
storeurl: {
                    __metadata: { type: 'SP.FieldUrlValue' },
                    Description: $scope.storedescr,
                    Url: $scope.storeurl
},
   }).success(function (data) {
       //resolve the new data
alert("Book has been added successfully.")
       dfd.resolve(data.d);
   }).error(function (data) {
       dfd.reject("failed to add book");
   });
   return dfd.promise;
}

// Completed add book

this.getuserid = function (username,$scope) {
   var dfd = $q.defer();
       $http.get(_spPageContextInfo.webAbsoluteUrl+"/_api/web/siteusers(@v)?@v="+username).success(function (data) {
       //resolve the new data
       dfd.resolve(data.d);
$scope.emp=data.d.Id;
   }).error(function (data) {
alert(data);
       dfd.reject("failed to get user id");
   });
   return dfd.promise;
}

while adding new item which is having peoplepicker field then we need selected user id so for that we have used getuserid function to get selected user id.




In my next post, will see how to update and delete item using REST API.

Here the complete source code.

Wednesday, 11 November 2015

Error Type: The name ‘InitializeControl’ does not exist in the current context

Hi, 

Today i want to explain how to resolve "The name ‘InitializeControl’ does not exist in the current context" error.

This error ever common when you try to build SharePoint solution from visual studio 2010/2013.

This error mainly comes when you do not have access to connect SharePoint site.

To confirm this, Try to create new SharePoint solution from visual studio, give site URL where you want to debug and click on connect. Then you will get the below error.

The local SharePoint server is not available. Check that the server is running and connected to the SharePoint farm.


It means that you do not have access to connect to SharePoint server from visual studio.

To resolve the above two issues  you should have content database access of config database and the web application.

Now, Go to SharePoint powershell management and execute the below script/queries

Add-SPShellAdmin -UserName <domain\username>

--This will add permissions to the config database in SharePoint 2010

$db = Get-SPContentDatabase -WebApplication <web application URL>

Add-SPShellAdmin -UserName <domain\username> -database $db.Id

Replace "<domain\username>" with the user who is going to work and <web application URL> with the web application.

Now you can happily start working on :).

Sunday, 18 October 2015

Carousel with content in SharePoint using AngularJS + REST API

Hi,

i was working AngularJS and REST API, i have thought to create dynamic carousel with the help of SharePoint list data.

1. I have created custom list with two extra columns namely a) ImgURL b) Caption both are single line of text column type.


2. Here am storing images in shared documents.

3. Now open the page and edit then add script editor web part to the page.

4. Add the below script to script editor

<script src="https://<site URL>/SiteAssets/Kendo/jquery.min.js"></script>
<script src="https://<site URL>/SiteAssets/Kendo/angular.min.js"></script>

<script data-require="angular-ui-bootstrap@*" data-semver="0.6.0" src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet" />
<script>
       
var myApp=angular.module('plunker', ['ui.bootstrap']);

        myApp.service('bookService', ['$http', '$q', function ($http, $q) {

    //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-RequestDigest'] = $("#__REQUESTDIGEST").val();
$http.defaults.headers.post['If-Match'] = "*";
this.GetDetails= function(listTitle,$scope)
{
var restUrl = _spPageContextInfo.webAbsoluteUrl+"/_api/web/lists/getbytitle('" + listTitle + "')/items";
$http({
method: 'GET',
url: restUrl,
headers: { "Accept": "application/json;odata=verbose" }
}).success(function (data) {
$scope.slides=data.d.results;
$scope.myInterval = 3000;
}). error(function (data) {
});
}
}]);
myApp.controller('CarouselDemoCtrl', function($scope, bookService) {
$scope.Getdetails=function() {
var getdata=bookService.GetDetails('carousel',$scope);
}
});
</script>
<style>
      .carousel-indicators{left:50%; display:none;}
      .carousel-indicators li {
        background-size : 42px 22px;
        width : 42px;
        height: 22px;
        border-radius : 0px;
        text-indent : 0px;
        background-repeat : no-repeat;
        background-position : center;
        cursor : pointer;
      }
      .carousel-indicators{left:50%;}
 .carousel-caption p{align:"center";}
</style>
    
<div ng-app="plunker">
    <div ng-controller="CarouselDemoCtrl" data-ng-init="Getdetails()">
      <carousel interval="myInterval">
        <slide ng-repeat="slide in slides" active="slide.active">
          <img ng-src="{{slide.ImgURL}}" style="margin:auto;width:835px;height:445px;" />
          <div class="carousel-caption">
            <p align="center">{{slide.Caption}}</p>
          </div>
        </slide>
      </carousel>
    </div>
</div>

5. Here the output:



CURD operations on list using RESTAPI and AngularJS in SharePoint 2013

Hi,

After long time i got time to write something about RESTAPI and AngularJS.

In this article, will try to explain about how to use RESTAPI and AngularJS for basic operation on a list i.e. Create, Update, Read, and Delete items in a list.

First we will get list item details

Here we go...

1. I have create a list named it as "Bookshelf"
2. Created with following columns ( Here i want to use most of the data types for this example)
     Title (its actually Book name) -- Single line of text
     BookSummary  -- Multiline of text
     Bookreleasedate  -- datetime
     Bookisready  -- Yes/No
     BookType -- Dropdown
     Emp(actually its author) -- People picker
     storeurl -- Hyperlink
3. I have entered few rows for testing purpose and list looks like this

4. Now i have written script to get list details using REST API and displaying data using AngularJS

AngularJS is a MVC based framework. So here i will use same pattern.

Here view is table which will display data which is coming from Model

The Controller in AngularJS is ng-controller.

Model in AngularJS is service which actually call REST API service and get the data.

                                             AngularJS MVC


5. Now create one page to display list data and named it as "GetListData.aspx"

6. Edit the page and add script editor.

7. Now add script to script editor as shown below

In angularJS first first we need to add angularJS script references (you can refer JS files either from online url or files which are download and upload to your sharepoint site)

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://<server>/site/UK/SiteAssets/Kendo/jquery.min.js"></script>
<script src="https://<server>/site/UK/SiteAssets/Kendo/angular.min.js"></script>
<script>

Now i have designed layout to display data as below

<div ng-app="bookapp">
<div id="maincontroler" ng-controller="bookctrl" class="form-horizontal" data-ng-init="Get()">
<div id="AngularJSexample" class="table-responsive">
 <table class="table table-bordered" id="AngularJSexample">
<tr>
  <th>Id</th>
  <th>Title</th>
  <th>Booksummary</th>
  <th>ReleaseDate</th>
  <th>Booktype</th>
  <th>URL</th>
 </tr>
 <tr ng-repeat="book in books">
  <td>{{book.Id}}</td>
 <td>{{book.Title }}</td>
 <td>{{book.Booksummary | makeUppercase}}</td>
 <td>{{book.ReleaseDate | date  }}</td>
 <td>{{book.Booktype}}</td>
 <td><a href={{book.storeurl.Url}}>{{book.storeurl.Description}}</a></td>
</tr>
</table>
</div>
</div>

now we need to load AngularJS app on page load as below

<script>
var myAngApp = angular.module('bookapp', []);
....
</script>

Now we need to load controller

<script>
var myAngApp = angular.module('bookapp', []);
myAngApp.controller('bookctrl', function($scope, bookService) {
.....
});
</script>

The controller responds to user input and performs interactions on the data model objects. The controller receives input, validates it, and then performs business operations that modify the state of the data model.

So, in view am calling Get() method with the help of ng-init it means that whenever page getting load ng-init will call the Get function.

this Get function triggered in the view and controller will respond to that call and get the data from model and send it back to view.

so now i need to declared Get function/method in the controller as below

<script>
var myAngApp = angular.module('bookapp', []); 
myAngApp.controller('bookctrl', function($scope, bookService) {
$scope.Get=function() {
var getdata=bookService.GetDetails('Bookshelf',$scope);
}
});
</script>

Here am using service as model which is responsible for managing application data.

Here i will show i declared my service.

myAngApp.service('bookService', ['$http', '$q', function ($http, $q) {
.............
}]);


In this service, i will call SharePoint REST API services to get list data.

Here the code to call REST API service to get list data.

myAngApp.service('bookService', ['$http', '$q', function ($http, $q) {

    //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-RequestDigest'] = $("#__REQUESTDIGEST").val();
$http.defaults.headers.post['If-Match'] = "*";
this.GetDetails= function(listTitle,$scope)
{
var restUrl = _spPageContextInfo.webAbsoluteUrl+"/_api/web/lists/getbytitle('" + listTitle + "')/items";
$http({
method: 'GET',
url: restUrl,
headers: { "Accept": "application/json;odata=verbose" }
}).success(function (data) {
$scope.books=data.d.results;
}). error(function (data) {
});
}
}]);


Now, whenever page getting load Get() function is called from view and controller will respond to that call from view and it will get the data from Model i.e. service and return the result to view.

In view, result is object which contains records. To display all the records we use ng-repeater as shown below

<tr ng-repeat="book in books">  
<td>{{book.Id}}</td>
<td>{{book.Title }}</td>
<td>{{book.Booksummary}}</td>
<td>{{book.ReleaseDate | date  }}</td>
<td>{{book.Booktype}}</td>
<td><a href={{book.storeurl.Url}}>{{book.storeurl.Description}}</a></td>
</tr>  


Now the final result will be as shown below




Here complete code




Part-2: How to add new item to SharePoint using REST API and AngularJS...