Monday, May 11, 2015

Manage your customizations with custom JS/CSS without changes

From time to time i see a lot of discussions about the Masterpages, Branding, Styles, Customizations, UI/UX in SharePoint/Office 365...
This article uses examples in my custom environment where i can explain how you can manage branding/styles and Customization changes in my SharePoint/Office 365 Sites and keep the environment regarding Out of the Box "Masterpage/Style files" being untouched, manageable and clean to constant the SharrePoint/Office365 upgrades.

This was done in Test environment, if you need to make change in production please reflect in the impact.

Important and recommend to read before you do something in Branding:

Every time you need to change the branding, think in all the impact and costs you could have and this means $$$ costs, this is the reason people talk about Customization Tax, because if you make changes this could have impact in newer version of your SharePoint/Office365 environment and that means COST.

There is a video from the ignite from Vesa Juvonen about "Deep Dive into Safe SharePoint Branding in Office 365 Using Repeatable Patterns and Practices" and shared in Channel9 explain all the impact and possible cost of changing the Branding and explains what is supported and not with a lot of recommendations.

Here a nice article talking about Branding from Chris O'Brien,

Video from ignite about Branding:

Deep Dive into Safe SharePoint Branding in Office 365 Using Repeatable Patterns and Practices

https://channel9.msdn.com/Events/Ignite/2015/BRK3164

PS: 
A lot of Designers/Dev/PowerUsers already the work with this approach in other ways, for them is not new stuff, but want to show different approaches for the same issues and explain how i work with this type of topic in Office 365 and SharePoint related.  
There is also a SharePoint Property call AlternateCssUrl that could do this trick, 

There is the 5 questions rule you should make your self every time you need to make something (Who, What, Where, When, Why).

It's always better to use Out of the box options and not reinvent the wheel.

Here is the output example of the changes made without changing Out of the Box files.

My Architecture definition for the Branding/Customization

Here is the Architectural View of the approach about Managing Branding/ Styles/ Customizations and functionalities in SharePoint/Office 365 using as base this SharePoint Provider App.


For this example used the 
Processlynx Custom Action and Ribbon Manager (here a Demo of the app in Office Store)

Process for implementation

DEV/Designer
  • This users are responsible to create the Customizations, Styles and functionalities in JS/CSS/images and other support files and include them in a Folder
  • If they have SharePoint Knowledge they should be able to configure the Out of box User Custom Actions provided by that SharePoint Object using UI SharePoint App, 

Site Administrator/PowerUser

  • This users are responsible to Manage the correct reference to Files
  • If they have the SharePoint Knowledge should be able to configure the Out of box User Custom Actions provided by that SharePoint Object using a SharePoint App,
This approach add new UserCustomActions in your SharePoint Webs Object where are located all the references to JS files, this files will manage the new designs styles/functionalities and can be propagated to all site or even site Collection.
Every time a new change is needed the Power User or site Administrator can remove or update the JS Files without the need of changes of OoB files and everything should work because in Background everything is Out of box and can assume the Office 365 Microsoft changes without errors.



Example:
This example explains how to use a JavaScript file to load dynamically a CSS in the header of the SharePoint Site to make the changes in the Styles. 

Creation of AddCSS.JS 

This JavaScript file add dynamically the CSS file "CustomCSS.css" to all headers of the SharePoint site by JQuery.

$(document).ready(function () {
    $("head").append("<link rel='stylesheet' href='" + _spPageContextInfo.webAbsoluteUrl + "/SiteAssets/CustomCSS.css' type='text/css' media='screen'>");
});


Creation of Custom.CSS

/* change the background of Ribbon Area */
.ms-cui-tabBody
{
background-color:#F2F2F2;
}



/* change the background of Left Menu */
.ms-core-listMenu-verticalBox
{
background-color: antiqueWhite !important;
}

/* change the size of contentPlaceholder*/
#s4-workspace{
width:60% !important;
position:absolute;
left: 20%;
}



/* change the background of SharePoint Site */
.ms-backgroundImage{
  background-color:#8CBF26; 
}


/* change the position of OoB SharePoint Search*/
#DeltaPlaceHolderSearchArea{
position:absolute;
left:50%;
top:10%;
}



Here the final CSS:
.ms-cui-tabBody
{
background-color:#F2F2F2;
}
.ms-core-listMenu-verticalBox
{
background-color: antiqueWhite !important;
}
#s4-workspace{
width:60% !important;
position:absolute;
left: 20%;
}
.ms-backgroundImage{
  background-color:#8CBF26; 
}
#DeltaPlaceHolderSearchArea{
position:absolute;
left:50%;
top:10%;
}
Include the files in SharePoint Library
Access to your Office 365 SharePoint and Site Assets library and add the following Files "AddCSS.JS" and "CustomCSS.css"



Add UserCustomAction to SharePoint Site
Using the Processlynx Custom Action and Ribbon Manager you can include your custom User Custom Action to refer your JS files.
Jquery.JS is mandatory to be 1, the second reference should be "AddCSS.JS" because uses JQuery code.


After the JavaScript is included in the SharePoint Object UserCustomAction by the SharePoint App, access to your Site and the changes in the MasterPage and Look and feel will be shown.


If you like to use Visual Studio and Declarative code you can use the option "Generate Declarative XML" to create the XML for your SharePoint Feature as shown.
You can also use JSOM or CSOM to make the same actions.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <CustomAction Id="Jquery" Title="Jquery" Description="" Location="ScriptLink"ScriptSrc="~Site/SiteAssets/Jquery.JS" Sequence="1" > </CustomAction>
</Elements>

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <CustomAction Id="AddCSS" Title="AddCSS" Description="" Location="ScriptLink"ScriptSrc="~Site/SiteAssets/AddCSS.JS" Sequence="2" > </CustomAction>
</Elements>

To better understand the full power of this approach "
UserCustomAction" about customization's a design and functionality without breaking policies and keep it easily manage by Users for future upgrades:


Hope you like this article, 
AndrƩ Lage

Tuesday, May 05, 2015

Get User Profile Properties and Update using JSOM

Since everyone of the Office 365 and SharePoint Community is in Microsoft ignite Conference to see the new launches of the products services, i would like to talk more specific to Office 365 Dev and API update that was launch some time ago with a nice article writed by Vesa Juvonen SharePoint user profile properties now writable with CSOM for the PnP community.

The good stuff about Office 365 is the constant updates and new releases of the API and new services to support the different Products inside Office 365 what turns a very strong Cloud Service and reliable for enterprises.
  
The solution created in SharePoint-Hosted get a specific user from the people picker control and return the associated properties from the User Profile, after update the Text you can use update the property if have Write permission, not all User profiles Properties has Write Permission, but all have Read Permissions.
this can be useful if you need to update specific Properties from User profile and avoid a loot of click to get the correct user and property.

There are some interesting links listed bellow in the article you can use to explore about this topic.

The solution was created using Napa and can be accessed in the following link http://aka.ms/Irhs4v.




Solution "Get User profile Properties and Update SharePoint App"


The Starting point was a creation of Napa App solution in my SharePoint Environment where i define the HTML and include the support API for this solution, since i user multiple controls i include the following support files:

  • Jquery
People Picker
  • clienttemplates.js
  • clientforms.js
  • clientpeoplepicker.js
  • autofill.js
SharePoint JSOM Objects
  • sp.runtime.js
  • sp.js
SharePoint User Profile Objects
  • sp.userprofiles.js





App Permission

You need to include in the App Write permissions from User Profile Service to be able update the properties associated with a user.




Social:

  • User Profiles - Write


The Solution picks a user from SharePoint return the Properties data associated where you can update, if has Write permissions.



If everything goes ok you should get a message with success message.





Here you have a Code Example to manage the User Profile properties and Update Property



var UserId = "";

$(document).ready(function () {
    initializePeoplePicker('peoplePickerDiv');
    //GetUser();
});
function changeFunc() {
    var selectBox = document.getElementById("mySelect");
    var selectedValue = selectBox.options[selectBox.selectedIndex].value;
    document.getElementById('TextContent').value = selectedValue;
}
function GetUser() {

    var select = document.getElementById("mySelect");
    var length = select.options.length;
    for (i = length; i > -1; i--) {
        select.remove(i);
    }
    getProperties(UserId.replace("#""%23"));
}

function getPropertyValue(LoginName, selectedValue) {
    $.ajax({
        url: getQueryStringParameter('SPAppwebUrl') + "/_api/SP.UserProfiles.PeopleManager/GetUserProfilePropertyFor(accountName=@v,propertyName='" + selectedValue + "')?@v=" + "%27" + LoginName + "%27",
        type: "GET",
        headers: { "ACCEPT""application/json;odata=verbose" },
        success: function (data) {
            document.getElementById("mySelect").options[document.getElementById("mySelect").selectedIndex].value = data.d.GetUserProfilePropertyFor;
        },
        error: function () {
            alert("Failed to get customer");
        }
    });
}

function getProperties(LoginName) {
    $.ajax({
        url: getQueryStringParameter('SPAppWebUrl') + "/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor(accountName=@v)?@v='" + LoginName + "'",
        type: "GET",
        headers: { "ACCEPT""application/json;odata=verbose" },
        success: function (data) {
            var x = document.getElementById("mySelect");
            var option;
            for (var i = 0; i < data.d.UserProfileProperties.results.length; i++) {
                option = document.createElement("option");
                option.text = data.d.UserProfileProperties.results[i].Key;
                option.value = data.d.UserProfileProperties.results[i].Value;
                x.add(option);
            }
        },
        error: function () {
            alert("Failed to get customer");
        }
    });
}

function SetCurrentUserProperties() {
    var clientContext = SP.ClientContext.get_current();
    var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
    var selectBox = document.getElementById("mySelect");
    var selectedValue = selectBox.options[selectBox.selectedIndex].text;
    peopleManager.setSingleValueProfileProperty(UserId, selectedValue, document.getElementById('TextContent').value);

    clientContext.executeQueryAsync(function () {
        getPropertyValue(UserId.replace("#""%23"), selectedValue);
        SP.UI.Notify.addNotification("properties updated!"false);
    }, function (sender, args) {
        alert(args.get_message());
    });
}
function initializePeoplePicker(peoplePickerElementId) {

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

    this.SPClientPeoplePicker_InitStandaloneControlWrapper(peoplePickerElementId, null, schema);
}

function getUserInfo() {

    var peoplePicker = this.SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan;

    var users = peoplePicker.GetAllUserInfo();
    var userInfo = '';
    for (var i = 0; i < users.length; i++) {
        var user = users[i];
        for (var userProperty in user) {
            userInfo += userProperty + ':  ' + user[userProperty] + '<br>';
        }
    }
    var keys = peoplePicker.GetAllUserKeys();
    UserId = users[0].Key;
    GetUser();
}
function getQueryStringParameter(param) {
    var params = document.URL.split("?")[1].split("&");
    for (var i = 0; i < params.length; i = i + 1) {
        var singleParam = params[i].split("=");
        if (singleParam[0] == param)
            return decodeURIComponent(singleParam[1]);
    }
    return "";

}


The solution was created using Napa and can be accessed in the following Link
http://aka.ms/Irhs4v

Some useful articles for this topic. Read or update user profile properties sample app for SharePoint
https://msdn.microsoft.com/en-us/library/office/dn894690.aspx
SharePoint user profile properties now writable with CSOM
http://blogs.msdn.com/b/vesku/archive/2014/11/07/sharepoint-user-profile-properties-now-writable-with-csom.aspx
SharePoint 2013: Get UserProfile Properties with REST API- from Vardhaman Deshpande
http://www.vrdmn.com/2013/07/sharepoint-2013-get-userprofile.html
How to: Use the client-side People Picker control in SharePoint-hosted apps
https://msdn.microsoft.com/en-us/library/jj713593.aspx

Hope you enjoy this article,
AndrƩ Lage