Thursday, 14 February 2013

SharePoint 2013: Provision AppPart/ClientWebPart to Page

Along with Apps, SharePoint 2013 has introduced App Parts (ClientWebParts) which give you the functionality of placing the app inside and iframe on a page. This way, you can view your app as a part of the page just like you would a normal webpart.

(The terms App Part and Client Web Part are interchangeable. I will be using both the terms in this post to emphasize the similarity)

Adding an App Part to the page from the SharePoint User Interface is pretty easy. You just put the page in edit mode and add the AppPart from the Insert Menu. Pretty similar to how you would add a regular WebPart.

Now what if you have to provision your AppPart in a Page or a PageLayout so that when a new page is created with a feature, your AppPart is automatically placed on that page?.

If you want to know how to add an app part to a page declaratively through the Elements.xml file, please have a look at my post here:
http://vrdmn.blogspot.in/2013/03/sharepoint-2013-declaratively-add-app.html


Lets look at how it can be done:

First and foremost, you will need to create an AppPart (Client WebPart) for your app. Here is a great tutorial on it:
http://msdn.microsoft.com/en-us/library/fp179921.aspx

Your Client WebPart xml file should be similar to this:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ClientWebPart Name="MyWebPart" Title="My WebPart Title" Description="Description" DefaultWidth="300" DefaultHeight="200">
<!-- Content element identifies the location of the page that will render inside the client web part
Properties are referenced on the query string using the pattern _propertyName_
Example: Src="~appWebUrl/Pages/ClientWebPart1.aspx?Property1=_property1_" -->
<Content Type="html" Src="~appWebUrl/Pages/Default.aspx?{StandardTokens}&amp;MyIntProp=_myIntProp_&amp;MyEnumProp=_myEnumProp_" />
<!-- Define properties in the Properties element.
Remember to put Property Name on the Src attribute of the Content element above. -->
<Properties>
<Property
Name="myIntProp"
Type="int"
RequiresDesignerPermission="true"
DefaultValue="7"
WebCategory="MyWebPartProperties"
WebDisplayName="My Integer Property">
</Property>
<Property
Name="myEnumProp"
Type="enum"
RequiresDesignerPermission="true"
DefaultValue="EnumValue1"
WebCategory="MyWebPartProperties"
WebDisplayName="My Enum Property">
<EnumItems>
<EnumItem WebDisplayName="Enum Value 1" Value="EnumValue1"/>
<EnumItem WebDisplayName="Enum Value 2" Value="EnumValue2"/>
</EnumItems>
</Property>
</Properties>
</ClientWebPart>
</Elements>


Here, I have created a Client Web Part with 2 Properties of type int and enum respectively.

Second, you have to make sure that your app is already installed on the Web on which you will be provisioning your page. The AppPart will only work if the app is installed on the same web.

Next, you will need the ProductId of your app. Here is how to find it:

1) Open your app in Visual Studio -> Right Click on the AppManifest.xml -> View Code


2) Here you will find the ProductId of your app in the following location:


3) Save your ProductId in a convenient location.

4) Now open your Page or Page Layout and add the following code to it:

<%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" MasterPageFile="~masterurl/default.master" Language="C#" meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document" %>
<%@ 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" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<WebPartPages:WebPartZone runat="server" AllowPersonalization="true" ID="WebPartZone2" FrameType="TitleBarOnly" Title="Bottom" Orientation="Vertical">
<ZoneTemplate>
<WebPartPages:ClientWebPart runat="server"
FeatureId="17297f8b-5333-46d8-8af6-78e282ea4f22"
ProductWebId="57278E26-4B76-4AC8-8EFE-C52FB279635E"
WebPartName="MyWebPart"
Title="My WebPart Title"
Description="WebPart Description"
WebPart="true">
<Properties>
<WebPartPages:ClientWebPartProperty Name="myIntProp" Value="7"></WebPartPages:ClientWebPartProperty>
<WebPartPages:ClientWebPartProperty Name="myEnumProp" Value="EnumValue1"></WebPartPages:ClientWebPartProperty>
</Properties>
</WebPartPages:ClientWebPart>
</ZoneTemplate>
</WebPartPages:WebPartZone>
</asp:Content>

In FeatureId, enter the ProductId which we got from the app earlier.

In ProductWebId, create a new random GUID and add it. 

You can even set default values for the App Part Properties.

5) That's It, Now deploy your page and see that the App Part is Provisioned on the Page.

Sunday, 3 February 2013

SharePoint 2013: Working with User Profiles & JavaScript CSOM

SharePoint 2013 has added a variety of functionality to the Client API. One of them is the ability to fetch User Profile data. Now you can directly query the user profiles and get the required data from the client site. This can be really useful if you are building apps.

If you want to use the REST API to work with user profiles, please see one of my other posts: http://www.vrdmn.com/2013/07/sharepoint-2013-get-userprofile.html

In this post, I will show you how to work with User Profiles using the JavaScript Client Object Model

Before we get started with the code, lets make couple of things sure. First,  you will need a reference to the following JavaScript files in your page:

<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.0.min.js"></script>
<script src="/_layouts/15/SP.Runtime.js"></script>
<script src="/_layouts/15/SP.js"></script>
<script src="/_layouts/15/SP.UserProfiles.js"></script>
view raw upScripts.html hosted with ❤ by GitHub

(jQuery is not required but I have added it because we will need it for the $.ajax function when doing REST queries)

Second thing, you will need the domain\username of the user you want to get the user profile data for. This can be easy to get if you are in an On-Prem environment. But if you are working with SharePoint Online, this can be quite tricky. Your username might not always be in the domain\username format. It is stored in the LoginName property if you are querying the Site User Information List:

https://yoursite.sharepoint.com/sites/pubsite/_api/Web/GetUserById(17)

and it is stored in the AccountName property if your querying the User Profile Service:

https://yoursite.sharepoint.com/sites/pubsite/_api/SP.UserProfiles.PeopleManager/GetMyProperties

In SharePoint Online, it will most probably be in the following format:

i:0#.f|membership|vardhaman@yoursite.onmicrosoft.com


Quick Note: If you are using the REST API, you will need to encode the username before you use it in your call. The encodeURIComponent( ) function can be helpful here.

Enough talking. Lets jump into some code right away:

1) Get Multiple User Profile Properties:

(function($){
$(document).ready(function(){
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
SP.SOD.executeOrDelayUntilScriptLoaded(loadUserData, 'SP.UserProfiles.js');
});
var userProfileProperties = [];
function loadUserData(){
//Get Current Context
var clientContext = new SP.ClientContext.get_current();
//Get Instance of People Manager Class
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
//Properties to fetch from the User Profile
var profilePropertyNames = ["PreferredName","PictureURL"];
//Domain\Username of the user (If you are on SharePoint Online)
var targetUser = "i:0#.f|membership|vardhaman@yoursite.onmicrosoft.com";
//If you are on On-Premise:
//var targetUser = domain\\username
//Create new instance of UserProfilePropertiesForUser
var userProfilePropertiesForUser = new SP.UserProfiles.UserProfilePropertiesForUser(clientContext, targetUser, profilePropertyNames);
userProfileProperties = peopleManager.getUserProfilePropertiesFor(userProfilePropertiesForUser);
//Execute the Query.
clientContext.load(userProfilePropertiesForUser);
clientContext.executeQueryAsync(onSuccess, onFail);
}
function onSuccess() {
var messageText = "\"Preffered Name\" property is " + userProfileProperties[0];
messageText += "\"PictureURL\" property is " + userProfileProperties[1];
alert(messageText);
}
function onFail(sender, args) {
alert("Error: " + args.get_message());
}
})(jQuery);
view raw upMult.js hosted with ❤ by GitHub


2) Get Single User Profile Property:

(function($){
$(document).ready(function(){
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
SP.SOD.executeOrDelayUntilScriptLoaded(loadUserData, 'SP.UserProfiles.js');
});
var userProfileProperty;
function loadUserData(){
//Get Current Context
var clientContext = new SP.ClientContext.get_current();
//Get Instance of People Manager Class
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
//Property to fetch from the User Profile
var propertyName = "PreferredName";
//Domain\Username of the user (If you are on SharePoint Online)
var targetUser = "i:0#.f|membership|vardhaman@yoursite.onmicrosoft.com";
//If you are on On-Premise:
//var targetUser = domain\\username
//Create new instance of UserProfileProperty
userProfileProperty = peopleManager.getUserProfilePropertyFor(targetUser, propertyName)
//Execute the Query. (No load method necessary)
clientContext.executeQueryAsync(onSuccess, onFail);
}
function onSuccess() {
var messageText = "\"Preferred Name\" property is " + userProfileProperty.get_value();
alert(messageText);
}
function onFail(sender, args) {
alert("Error: " + args.get_message());
}
})(jQuery);
view raw upsing.js hosted with ❤ by GitHub


3) Get User Profile Properties of the Current User:

(function($){
$(document).ready(function(){
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
SP.SOD.executeOrDelayUntilScriptLoaded(loadUserData, 'SP.UserProfiles.js');
});
var userProfileProperties;
function loadUserData(){
//Get Current Context
var clientContext = new SP.ClientContext.get_current();
//Get Instance of People Manager Class
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
//Get properties of the current user
userProfileProperties = peopleManager.getMyProperties();
clientContext.load(userProfileProperties);
//Execute the Query.
clientContext.executeQueryAsync(onSuccess, onFail);
}
function onSuccess() {
alert(userProfileProperties.get_displayName());
}
function onFail(sender, args) {
alert("Error: " + args.get_message());
}
})(jQuery);


4) Get Properties of Multiple Users in Single Request:

(function($){
$(document).ready(function(){
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
SP.SOD.executeOrDelayUntilScriptLoaded(loadUserData, 'SP.UserProfiles.js');
});
var userProfileProperties = [];
//Array containing domain\usernames of multiple users. You can get the usersnames any way you want.
var targerUsers = ["i:0#.f|membership|vardhaman@yoursite.onmicrosoft.com","i:0#.f|membership|demouser1@yoursite.onmicrosoft.com"];
//If you are on On-Premise:
//var targerUsers = ["domain\\username","domain\\demouser1"];
function loadUserData(){
//Get Current Context
var clientContext = new SP.ClientContext.get_current();
//Get Instance of People Manager Class
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
//Property to fetch from the User Profile
var propertyName = "PreferredName";
for(var i=0;i<targerUsers.length;i++){
//Create new instance of UserProfileProperty
userProfileProperties[i] = peopleManager.getUserProfilePropertyFor(targerUsers[i], propertyName);
}
//Execute the Query. (No load method necessary)
clientContext.executeQueryAsync(onSuccess, onFail);
}
function onSuccess() {
var messageText = "";
for(var i=0;i<userProfileProperties.length;i++){
messageText += "\"Preffered Name\" property is " + userProfileProperties[i].get_value();
}
alert(messageText);
}
function onFail(sender, args) {
alert("Error: " + args.get_message());
}
})(jQuery);
view raw uponecall.js hosted with ❤ by GitHub


For this last example, Let's observe the XML which is sent to the server:

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="15.0.0.0" ApplicationName="Javascript Library">
<Actions>
<ObjectPath Id="1" ObjectPathId="0"></ObjectPath>
<Method Name="GetUserProfilePropertyFor" Id="2" ObjectPathId="0">
<Parameters>
<Parameter Type="String">i:0#.f|membership|vardhaman@yoursite.onmicrosoft.com</Parameter>
<Parameter Type="String">PreferredName</Parameter>
</Parameters>
</Method>
<Method Name="GetUserProfilePropertyFor" Id="3" ObjectPathId="0">
<Parameters>
<Parameter Type="String">i:0#.f|membership|demouser1@yoursite.onmicrosoft.com</Parameter>
<Parameter Type="String">PreferredName</Parameter>
</Parameters>
</Method>
</Actions>
<ObjectPaths>
<Constructor Id="0" TypeId="{cf560d69-0fdb-4489-a216-b6b47adf8ef8}"></Constructor>
</ObjectPaths>
</Request>
view raw up.xml hosted with ❤ by GitHub


With this, we can confirm that only one call was made to the server to retrieve the properties of multiple users. This way, you can even retrieve multiple properties of multiple users in one call.