Showing posts with label Architecture. Show all posts
Showing posts with label Architecture. Show all posts

Saturday, November 7, 2015

Embed an Office Group Conversation into SharePoint Online using a JavaScript App, CORS, and the Office 365 Unified API

I am very happy to publish a post where I can use the brand new Office 365 unified API to embed a conversation of the brand new Office Group into a SharePoint Online page!


1 - Architecture
A SharePoint Online html page sends an Ajax request to the Office 365 Unified API and get the data of the Office Group.(The SharePoint Online site and the Office Group belong of course to the same Office 365 tenant).
It seems simple at first but it wasn't possible since recently. Why?
Because of the different hostheaders of the different part of Office 365, You can check while navigating through Office 365. For example, recently I created an Office 365 environment and first registered as contososoftware. My domain was contososoftware.onmicrosoft.com. Then I had to buy a domain to activate Yammer (You should have a verified domain in Office 365 to have Yammer activated to Enterprise. The "onmicrosoft.com" is not a domain but an Office 365 tenant name or a default domain and cannot be used to activate Yammer Enterprise. So I bought marccharmois.com.
(roughly 14 US$ a year, not a big deal). After that, when I go to
  • My mail, My calendar, My contacts (people), My Tasks, the Url starts with https://outlook.office.com/owa/?realm=marccharmois.com#path=
  • NewsFeed, OneDrive, Sites, Delve, Video, the Url starts with https://contososoftware-my.sharepoint.com/
  • Yammer, the Url starts with https://www.yammer.com/marccharmois.com/
  • a SharePoint Online site, the Url starts with https://contososoftware.sharepoint.com/
So if I want to deploy an html page somewhere Online in Office 365 and make an Ajax request to a part that has a different hostheader (for example displaying some of my mails into a SharePoint Online page), it normally doesn't work because crosss domain Ajax requests are forbidden by the browsers. It is worse if my HTML page is deployed on a server.
Cross-origin resource sharing (CORS) is a mechanism that allows that. CORS defines a way in which a browser and server can interact to safely determine whether or not to allow the cross-origin request. It allows for more freedom and functionality than purely same-origin requests, but is more secure than simply allowing all cross-origin requests. It is a recommended standard of the W3C. (Wikipedia)
Of course Microsoft sets up Cors within Office 365 in order developers to be allowed to performed Ajax requests without being bothered by the different hostheaders of Office 365.
The CORS of Office 365 is based on Microsoft Azure:
  • You register an App in Azure and you are provided with ID's that you place in your code.
  • In the Azure App, you also reference the Urls of the pages that are planned to call the Office 365 API with cross domain requests
This double registration allows the CORS of Office 365 to properly work and also manage the oauth authentication.

That leads to the fact that the term "Application" or App has a double meaning. It could be:
  • The pages that you have deployed somewhere on a server or within the Office 365 cloud (for example SharePoint Online) and that performs the Ajax requests and displays the data. Let's call this the physical part of the App, or the physical App
  • The Application that you had to add to Azure for getting the Office 365 CORS and the oauth authentication to work. Let's call that the virtual part of the App, or the virtual App
In the case of this tutorial, my physical App is just an html page added to a SharePoint Online team site using explorer mode (WebDAV protocol)
It is very similar of what I did when I embedded a Yammer conversation within SharePoint Online. This is super convenient because I can program and deploy quickly a physical App in Office 365 based on an html page that uses only Javascript!
It could seem a little bit difficult to understand at this point of the post, but I will explain all the operations to do it step by step with all the detailed screenshots as usual, so don't worry. I also did a diagram that can summarize all this in a clearer way:



2 - What you need to do this tutorial
You need:
Once you can access to an Office 365 environment using a business account with global administrator privileges, the first thing you need to do is register your application with Azure AD.

3 - Creating the (virtual) App within Microsoft Azure
3.1 Create a new subscription to Microsoft Azure
To Navigate to the Azure Portal you can use this link: https://manage.windowsazure.com/
you can also access to it within 0ffice 365:
Log on to Office 365. From the Home page, select the Admin icon to open the Office 365 admin center.



In the menu page along the left side of the page, scroll down to Admin and select Azure AD.



If prompted, log in using the credentials you created for your O365 subscription.



After logging in, you should see a screen notifying you that you do not have a subscription



Create a new subscription.
If you're using a trial version of Office 365, you'll see a message telling you that Azure AD is limited to customers with paid services. You can still create a trial 30-day Azure subscription at no charge, but you'll need to perform a few extra steps:
Select your country or region, and then choose Azure subscription.
Enter your personal information. For verification purposes, enter a telephone number at which you can be reached, and specify whether you want to be sent a text message or called.
Once you've received your verification code, enter it and choose Verify code.
Enter payment information, check the agreement, and select Sign up.
Your credit card will not be charged.
Do not close or refresh your browser while your Azure subscription is being created.
Once your Azure subscription is created, choose Portal.
The Azure Tour appears. You can view it, or choose X to close it.
You should now see all items in your Azure subscription. It lists a directory with the name of your Office 365 tenant.

3.2 Register your App in Azure Management Portal

Once signed in, follow these instructions:
Click the Active Directory node in the left column and select the directory linked to your Office 365 subscription.



Select the Applications tab and then Add at the bottom of the screen.





On the pop-up, select Add an application my organization is developing.



Choose an explicit name for your app,(I took Embed-OfficeGroup), and select Web application and/or web API as its Type. Then click the arrow to continue.



The value of Sign-on URL is the URL where your application will be hosted. As I will call the App using JavaScript within a SharePoint page, I put the Url of my SharePoint site.
The value of App ID URI is a unique identifier for Azure AD to identify your app. You can use http://{your_subdomain}/{YourAppName}, where {your_subdomain} is the subdomain of .onmicrosoft you specified while signing up for your Office 365 Developer Site. Then click the check mark to provision your application. For example, my Office 365 global admin account is, in this tutorial case:

marc.charmois@marccharmois.onmicrosoft.com





Now that your app has been provisioned, select the Configure tab.



Scroll down to the permissions to other applications section and click the Add application button.



In this tutorial, we want our App to read the conversation of an Office group. To access to the Office Group we have to use the new Office 365 Unified API.
Click the plus sign in the application's row and then click the check mark at the top right to add it. Then click the check mark at the bottom right to continue.



In the Office 365 Unified API row, select Delegated Permissions, and in the selection list, choose Read All Groups.



Click Save to save the app's configuration.







3.3 Configure your app to allow the OAuth 2.0 implicit grant flow


In order to get an access token for Office 365 API requests, your application will use the OAuth implicit grant flow. You need to update the application's manifest to allow the OAuth implicit grant flow because it is not allowed by default.
Select the Configure tab of your application's entry in the Azure Management Portal.


Using the Manage Manifest button in the drawer, download the manifest file for the application and save it to your computer.









Open the manifest file with a text editor. Search for the oauth2AllowImplicitFlow property. By default it is set to false; change it to true and save the file.



Using the Manage Manifest button, upload the updated manifest file.







You've now successfully registered your application with Azure AD.

4 - Coding the SharePoint page (physical App) that's using the Azure App (Virtual App)

4.1 Creating the app.html page

In your 0ffice 365 environment, create a new site collection. Once it's done, navigate to the "Site Assets" library. Open the library in Explorer mode




and using the window navigate to the root folder of your site. Create an html page within the folder and name it app.html



You can now open this page with Visual Studio or Notepad ++ and start to programm the displaying of the Office Group conversation



You can find the complete app.html page in the dedicated Github repository

4.2 Determine the resource endpoint

In order to make any API requests, you'll need to determine the correct endpoint of the resource you want to use. The endpoint you'll use is determined by what information you want from Office 365. Refer to the API reference documentation to get the endpoint you want.
Mail API reference
Contacts API reference
Calendar API reference
Files API reference
Alternatively, you can take advantage of the Office 365 unified API (preview) to access all of the APIs from a single endpoint, https://graph.microsoft.com. Refer to the Office 365 unified API reference to browse all of the supported endpoints.
For this sample, we will use the Office 365 unified API to get a conversation of an Office Group. The endpoint for this operation is
https://graph.microsoft.com/beta/{your_domain}/groups, where {your_domain} is the domain you specified while signing up for your Office 365 Developer Site. In my case marccharmois.onmicrosoft.com.

My endpoint is: https://graph.microsoft.com/beta/marccharmois.onmicrosoft.com/groups

4.3 Get an access token from Azure

Office 365 uses OAuth 2.0 tokens issued by Azure AD to authenticate JavaScript clients. Tokens are obtained using the OAuth 2.0 implicit grant flow. Using implicit grant, your application requests an access token from Azure AD for the currently signed-in user by sending the user to an authorization URL where the user signs in with their Office 365 credentials and then is redirected back to the app with the access token in the URL.
The following function builds the authorization URL and navigates to it to begin the authentication process.

function requestToken() { 
  // Change clientId and replyUrl to reflect your app's values 
  // found on the Configure tab in the Azure Management Portal. 
  // Also change {your_subdomain} to your subdomain for both endpointUrl and resource. 
  var clientId    = 'e77659cc-bf72-4276-bd33-bdd876660a74';//ID of your App in Azure
  var replyUrl    = 'https://marccharmois.sharepoint.com/sites/intranet/app.html'; //my sharepoint page that requests an oauth 2 authentification and data
  //It is also referenced in the REPLY URL field of my App in Azure
  var endpointUrl = 'https://graph.microsoft.com/beta/marccharmois.onmicrosoft.com/groups';
  //var endpointUrl = 'https://marccharmois-my.sharepoint.com/_api/v1.0/me/files';//getting files from SharePoint
  //var endpointUrl = 'https://outlook.office.com/api/V1.0/me/messages';//getting messages  from outlook
  var resource = "https://graph.microsoft.com/";
  //var resource = "https://marccharmois-my.sharepoint.com"; //getting files from SharePoint
  //var resource = "https://outlook.office.com"; //getting messages  from outlook
  
  var authServer  = 'https://login.windows.net/common/oauth2/authorize?';  
  //var authServer  =  'https://login.microsoftonline.com/common/oauth2/authorize?';//this works either
  var responseType = 'token'; 

  var url = authServer + 
            "response_type=" + encodeURI(responseType) + "&" + 
            "client_id=" + encodeURI(clientId) + "&" + 
            "resource=" + encodeURI(resource) + "&" + 
            "redirect_uri=" + encodeURI(replyUrl); 

  window.location = url; 
}

At this point what's going on?
When I use this function, I am redirected to the same page, but with the acces token in the Url as a parameter:





I can even display the token

var urlParameterExtraction = new (function () { 
  function splitQueryString(queryStringFormattedString) { 
    var split = queryStringFormattedString.split('&'); 
    // If there are no parameters in URL, do nothing.
    if (split == "") {
      return {};
    } 
    var results = {}; 
    // If there are parameters in URL, extract key/value pairs. 
    for (var i = 0; i < split.length; ++i) { 
      var p = split[i].split('=', 2); 
      if (p.length == 1) 
        results[p[0]] = ""; 
      else 
        results[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " ")); 
    } 
    return results; 
  } 
  // Split the query string (after removing preceding '#'). 
  this.queryStringParameters = splitQueryString(window.location.hash.substr(1)); 
})(); 



function displayToken(){
// Extract token from urlParameterExtraction object.
var token = urlParameterExtraction.queryStringParameters['access_token'];
alert('token : \n'+ token);
}

Now that I have the token, here is the code to request the Office 365 Unified API to get all the specific conversation of an Office Group:
function getToken(){

var token = urlParameterExtraction.queryStringParameters['access_token'];
return token;
}

function getGroupsFromO365() { 
  try 
  { 
    //var endpointUrl = 'https://graph.microsoft.com/beta/marccharmois.onmicrosoft.com/groups'; //getting all groups to get the ID of the group you want
 //var endpointUrl = 'https://marccharmois-my.sharepoint.com/_api/v1.0/me/files';//getting files from SharePoint
 //var endpointUrl = 'https://outlook.office.com/api/V1.0/me/messages';//getting messages from SharePoint
 var endpointUrl = "https://graph.microsoft.com/beta/contoso.com/groups('4eba7454-b490-46ae-bb5e-774efaec7c6f')/conversations('AAQkADZjOTdkMTIwLWFjNTItNDUyYy05MTc4LTg1NmJmMDk1MjYxOQAQAD_Ql6WI-GlHs3VmVZsV3QA=')/threads('AAQkADZjOTdkMTIwLWFjNTItNDUyYy05MTc4LTg1NmJmMDk1MjYxOQMkABAAP5CXpYj8aUezdWZVmxXdABAAP5CXpYj8aUezdWZVmxXdAA==')/Posts";

    var xhr = new XMLHttpRequest(); 
    xhr.open("GET", endpointUrl); 
    var myToken = getToken();
    // The APIs require an OAuth access token in the Authorization header, formatted like this: 'Authorization: Bearer '. 
    xhr.setRequestHeader("Authorization", "Bearer " + myToken); 

    // Process the response from the API.  
    xhr.onload = function () { 
      if (xhr.status == 200) { 
     //alert('data received');
  var message="";  
  var object = JSON.parse(xhr.response); 
  for(i=0;i<object.value.length;i++){
  message+='From: ' + object.value[i].From.EmailAddress.Name + '<BR>';
  message+='At: ' + object.value[i].CreatedDateTime + '<BR>';  
  message+= object.value[i].Body.Content + '<BR>';  
  }
        //var formattedResponse = JSON.stringify(JSON.parse(xhr.response), undefined, 2);
        document.getElementById("results").innerHTML = message;
      } else { 
        document.getElementById("results").textContent = "HTTP " + xhr.status + "<BR>" + xhr.response; 
      } 
    } 
    // Make request.
    xhr.send(); 
  } 
  catch (err) 
  {  
    document.getElementById("results").textContent = "Exception: " + err.message; 
  } 
}
Now, you can display this app.html into any SharePoint page by calling it within an iframe tag...
<span style="display:block;margin-bottom:3px;font-size;13px;">Embedded Office Group Conversation:</span> <iframe src="https://marccharmois.sharepoint.com/sites/intranet/app.html" style="height:400px;width=300px;border:solid 1px silver"></iframe>
As I did for this Web Part page;
5 - Aknowledgements

Tuesday, October 27, 2015

Programmatically post a message to Yammer from SharePoint Online

In my previous post, I showed how to programmatically embed a Yammer feed into a SharePoint Online page, using the JavaScript SDK for Yammer and the Yammer REST API. In this post I will show how to post a message into a Yammer group from a SharePoint Online page, using again the JavaScript SDK for Yammer and the Yammer REST API.



1 - Adding a new feature to post a message to a Yammer group, from SharePoint on line


In my previous post (the one for displaying the Yammer feed into SharePoint Online) I have explained why you have to create a Yammer App and how to do it. I also explained the basis of programming for Yammer with the Yammer Javascript SDK and the REST API. I advice you to do the previous tutorial.

Here, I will just explain some pieces of the code. You will find the complete page on the dedicated GitHub repositery.

The first function gets the Yammer topic value (selected by the user with the radio button). It is the place to manage authentication if needed. I have managed authentication for the getMyFeed function. It is explained also in the previous post.

function postMessage() {
   topicToPost=getTopicValue();
   yamPostRequest(this);
   /*If needed you have to manage authentication as in the script "authentication" I used for getting the feed (line 130)*/
   }

then you send the post to Yammer after having checked that no elements are missing. Because you cannot send a topic with an empty value you have to check if there is a topic:
if (topicToPost.length>0){
     yam.platform.request(
     { 
       url: "https://api.yammer.com/api/v1/messages.json"
       , method: "POST"
       , data: {
         "body" :  msg_value
        ,"group_id" : groupID
        ,topic1 : topicToPost
        ,"cc": "[[user:1557787745]],[[user:1557794361]]"//use your own user ID to notify them
        //,direct_to_id : "1537569057" seems not to work
        ,"skip_body_notifications" : "true"
      }
       , success: function (msg) { 
       document.getElementById('msg_body').value="";
      //alert("Post was Successful!"); 
      }
       , error: function (msg) { //alert("Your message cannot be published\nThe request to Yammer didn't work... "); 
      }
     })
  }else {
      //if there is no topic you do the same request except you avoid the "topic1" parameter
  }

Friday, October 16, 2015

Branding a SharePoint Online team site using the SharePoint JavaScript Object Model

In this post I will show how to use the JavaScript library code (JavaScript Client Object Model: SP.Runtime.js and SP.js) for easily branding a SharePoint Online team site while respecting the new pattern and practices. This is a third approach compared with the two ones currently recommended by Microsoft for SharePoint Online, that is to mean: NCSSs versus Add-Ins.

I will show in the following tutorial how to easily perform some of the main operations done by the Add-in while avoiding most of the Add-In way complications.

For a complete round regarding the classical approaches, you can visit this post: Branding a SharePoint Online Team Site
You can also read this post that gives a more accurate overview of the recommendations: Latest Advice on Office 365 Branding.




1 - Add-ins versus NCSSs

If you read carefully the MSDN documentation (updated August 2015 the 12th) exposing the recommendations regarding the use of the No-Code Sandboxed Solution or NCSS (aka a .wsp containing no server code and deployed in the Solution store at the Site Collection level), versus the use of the SharePoint Add-Ins (formerly called Apps for SharePoint), you can summarize it with the following conclusions:

Microsoft recommends to use Add-Ins whenever you can, but recommends NCCSs for branding as the easiest way to do it.

Regarding the Add-In approach, you can already review the material available on the Office 365 Developer Patterns and Practices:

Video on Channel 19: Alternate CSS and set site logo

Associated source code for the Add-In available on GitHub

You will notice that in the Office 365 Developer Patterns and Practices sample, the Add-In :
  • Change programmatically (Imperative approach) the logo and the CSS references for the site

    web.AlternateCssUrl = web.ServerRelativeUrl + "/SiteAssets/contoso.css";
    web.SiteLogoUrl = web.ServerRelativeUrl + "/SiteAssets/pnp.png";
    web.Update();
    web.Context.ExecuteQuery();

  • Upload programmatically (Imperative approach) the logo and the CSS files for the site

    // Use CSOM to upload the file in
    FileCreationInformation newFile = new FileCreationInformation();
    newFile.Content = System.IO.File.ReadAllBytes(cssFile);
    newFile.Url = "contoso.css";
    newFile.Overwrite = true;
    Microsoft.SharePoint.Client.File uploadFile = assetLibrary.RootFolder.Files.Add(newFile);
    web.Context.Load(uploadFile);
    web.Context.ExecuteQuery();

    // Get the path to the file which we are about to deploy
    string logoFile = System.Web.Hosting.HostingEnvironment.MapPath(
    string.Format("~/{0}", "resources/pnp.png"));

    // Use CSOM to upload the file in
    newFile = new FileCreationInformation();
    newFile.Content = System.IO.File.ReadAllBytes(logoFile);
    newFile.Url = "pnp.png";
    newFile.Overwrite = true;
    uploadFile = assetLibrary.RootFolder.Files.Add(newFile);
    web.Context.Load(uploadFile);
    web.Context.ExecuteQuery();

Then you can figure out the pros and the cons of these two approaches:

Add-Ins:
  • The pros
    • no impacts regarding Microsoft updates:
      As Add-Ins are using imperative approach (doing things programmatically) the OOTB templates of SharePoint (Web Part Page Templates, Page Layouts, default.aspx page, Master Pages) are preserved so as any update from Microsoft regarding these templates will have no impacts on the customizations performed using Add-Ins. That is to mean: no custom Master Page anymore, no CAML anymore, ever!
    • The branding action could be perform by a super user:
      It seems that you can leverage the permission for the users just for the use of the Add-In so as the users to be able to activate the branding of a site themselves without belonging to the Site Collection Administrators.
  • The cons
    • NCSSs are more powerful:
      As Add-Ins are using imperative approach (doing things programmatically) and NCSSs can use both imperative (Client side of course with SharePoint JavaScript library code) and declarative approach (modifying HTML, CSS within the custom templates and CAML for deploying and registering these custom templates in the libraries), there are numerous things that you can do with NCSSs that you cannot do using Add-Ins. That is to mean: custom Master Pages, custom pages, with all the HTML and the server controls you want within the custom pages.
    • Add-Ins require more time and more knowledge:
      Add-In model is completely new and different from classical approaches, thus technical team will have to enhance knowledge to be really comfortable with these new approaches. As said in the MSDN documentation when using Add-Ins remote imperative approach "it would take a considerable amount of work to create this code".
      Not only the SharePoint team will be involved, but also people working in the infrastructure and network areas because setting the necessary things regarding network and authentication for getting Add-ins work properly in a company requires also time and knowledge.
No-Code Sandboxed Solutions (aka NCSSs):
  • The Pros
    • Although the new Add-Ins model for SharePoint (formerly called Apps) is documented by Microsoft as the the recommended way for customizing SharePoint, you can see in the official MSDN documentation that among the scenarios recommended by Microsoft for using No-Code Sandboxed Solutions instead of Add-Ins (formerly called Apps), Branding of SharePoint Sites is one of them:

      [... SharePoint users often want to give their SharePoint sites, including their SharePoint Online sites, a custom appearance with their own colors, styles, layouts, and logos. This is generally easier to do with NCSSs than with SharePoint Add-ins.
      ...] MSDN SharePoint Add-ins compared with SharePoint solutions

      But NCSSs compared with Add-Ins are not only an easier way for branding a SharePoint Online site. Regarding branding, Add-Ins are more limited:

      [...
      A SharePoint Add-in has declarative control over the appearance of only its own add-in web. For the host web, it can declaratively add only ribbon buttons and menu items (and add-in parts). Any other changes to a host web or its parent site collection, tenancy, or on-premisesSharePoint web application has to be done with code or script that uses one of the SharePoint's client object models. For example, new icons or CSS files would have to be programmatically deployed. This code could be run from the add-in itself after it is installed, or it could run in the add-in installation event handler. But it would take a considerable amount of work to create this code.
      In addition, the add-in would need site collection-scoped permissions to change any websites outside its own add-in web and host web, and it would need tenant-scoped permissions to change more than just its parent site collection. A branding NCSS, however, can be deployed and activated to any site collection; and it could consist of only a few purely declarative components.
      ...] (MSDN)
  • The Cons
    • As your customized pages are not based on a OOTB SharePoint template anymore, if any update of the SharePoint templates is performed by Microsoft, these updates won't be taken into account by your customized page. You will miss new features of the product or maybe even lose current ones depending on the Microsoft updates. So you might perform sometimes unexpected maintenance operations for recovering all the features of the SharePoint product. (it might not be the case with custom master pages based on the use of the Design Manager with which you can convert an.html file into a SharePoint 2013 master page, a .master file. I have to check this more seriously. By the way, I planned to redo the same post using the SharePoint Publishing Features).
    • Using NCSS for branding a SharePoint Online team site will require the privileges of Site Collection Administrator and cannot be performed by a super user.
2 - A third approach: using JavaScript Object Model within a Site Page
I will show in the following tutorial how to easily perform some of the main operations done by the Add-in while avoiding most of the Add-In way complications. We will :
  • Upload the new logo of the site manually
  • Upload the new css of the site manually
  • Create the page for branding the site manually
  • Add Javascript code to the page in order to change automatically, the Logo, the css and the Master Page of the site.

2.1 Upload the new logo of the site manually

Use Bing or Google images to find a logo of the Contoso company. Try to find one based on a .gif or a .png with transparency (Contoso-Blue.png is fine).
When it's done, go to the SharePoint Online team site you want to customize (you can easily get a trial environement for 30 days). You need to be administrator of the Site Collection.
Then, create a folder called "images" in the site collection "Style Library" and Upload the Contoso logo image in the folder.






Don't forget to check in the logo image, in order visitors of the site will be able to see it. (if the logo image is checked out, only the administrator of the site will be able to see it).





2.2 Upload the new css of the site manually

Create a file called contoso.intranet.css and paste the following css within the file:

#siteIcon{margin-top:0px;}
#titleAreaBox{margin-top:0px;margin-left:20%;margin-right:20%;background-color:rgba(242, 242, 242, 1);}
#titlerow{background-color:rgba(242, 242, 242, 1);}
.ms-core-listMenu-horizontalBox{margin-top:30px;margin-left:-200px;}
.ms-breadcrumb-box {padding-top: 5px;padding-bottom: 5px;height:30px;}
#SearchBox{margin-left:200px;margin-right:-50px;}
#suiteBarDelta{margin:0px;margin-top:0px;}
#DeltaPlaceHolderSearchArea{padding-top:45px;}
.ms-srch-sb > input {width: 140px;}

Then, create a folder called "css" within the site collection "Style Library", and Upload the file within the "css" folder.




You can see that the .css file has been properly added within the folder, but it is checked out so only administrator will be able to see it. We have to check it in.






2.3 Creating the Site Page for branding the site

Navigate to the "Site Contents" page of your SharePoint Online team site, and locate the "Site Pages" library.


Open the Site Pages library and start creating a new Web Part page.


Call the page "Brand this Site"



The page appears in "edited" mode



Stop editing the page and note that the page was unexpectedly created within the "Site Assets" library.






2.4 Open the Site Page, using WebDAV protocol for modifying the source code

Now we are going to edit the page source code using WebDAV protocol. This is perfectly allowed since you will see in the page properties something called "Open with Microsoft SharePoint Designer".



If you have SharePoint Designer you can now open the page with it. If not, I will show you a trick to edit the page source code using the webDAV protocol with Notepad++. (Note that for me, depending on the machines I am working on, sometimes, clicking on the link "Open with Microsoft SharePoint Designer" while not having SharePoint Designer installed on the machine, leads to open the file in WebDAV protocol within the Notepad!)

Navigate to the Site Assets Library, and within the "Library" Tab of the Ribbon, locate the button called "Open With Explorer".



If you click on this button (be patient it takes time, and sometimes you have 2 warning pop-ups to close, and even to add the SharePoint Online domain in the trusted sites of your Internet Explorer) a Microsoft OS Explorer Window is opening where you can see the SharePoint files! (this is just amazing because actually the SharePoint files are stored, whether in the SharePoint content databases, or just as a link in the content databases pointing to the SharePoint server files system).
Note that this only works with Internet Explorer.
Now you can see the files within a Microsoft Explorer Window, you can as usual, right-click one of them, and choosing the option "Open with", open the file using WebDAV protocol with Notepad, Notepad++ or Visual Studio, or any HTML Code Source Editor of your choice.



2.5 Add Javascript and HTML code to the page in order to change automatically, the Logo, the css and the Master Page of the site.

Now we have to:
  • Reference the JQuery Library.
  • Create the code to brand and of course, unbrand the site.

First, create a folder called "scripts" in the "Style Library" of the Site Collection and paste in it the jQuery-1.9.1.min.js. You can find this file within an Add'In (App) project created with Visual Studio. Any else jQuery library could be fine however, since we use just basic jQuery features in this tutorial code.



The complete source code of the brand-this-site.aspx Web Part Page and the .ccs file are available @ the dedicated GitHub repositery

Within your .aspx page open in the code source editor with WebDAV protocol, locate the last meta tag at the beginning of the page within "PlaceHolderAdditionalPageHead" section:

    <meta name="CollaborationServer" content="SharePoint Team Web Site" />
and ad the reference to the jQuery library.
Be careful to point to the right location depending of your Site Collection and Site Name. In my case, here is the code of the section within Visual Studio:
    <meta name="CollaborationServer" content="SharePoint Team Web Site" />
    <!-- Added using WebDAV mode by Marc Charmois --->
    <script type="text/javascript" src="/Sites/intranet2/Style%20Library/scripts/jquery-1.9.1.min.js"></script>
    <!--script type="text/javascript" src="/_layouts/15/sp.runtime.js"</script-->/script>
    <!--script type="text/javascript" src="/_layouts/15/sp.js"</script-->/script>
<!-- end of the WeDAV customization -->
<sharepoint:scriptblock runat="server">
Notice that I used to reference the JSOM libraries, but I stopped since there are referenced in the Web Part page source code of SharePoint Online.

Then, just below the <asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server"> opening tag, paste the JavaScript code and replace the paths to the files with the ones corresponding to your SharePoint Online team site:

<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <script>

        function branding() {
            try {
                var context = new SP.ClientContext.get_current();
                var web = context.get_web();
                web.set_masterUrl('/sites/intranet2/_catalogs/masterpage/oslo.master');
                web.set_siteLogoUrl('/sites/intranet2/Style%20Library/images/Contoso-Blue.png');
                web.set_alternateCssUrl('/sites/intranet2/Style%20Library/CSS/Contoso.Intranet3.css');
                web.update();
                context.executeQueryAsync(onQuerySucceeded, onQueryFailed);
            } catch (error) {
                alert(error);
            }
        }

        function unBranding() {
            try {
                var context = new SP.ClientContext.get_current();
                var web = context.get_web();
                web.set_masterUrl('/sites/intranet2/_catalogs/masterpage/seattle.master');
                web.set_siteLogoUrl('/_layouts/15/images/siteIcon.png?rev=40');
                web.set_alternateCssUrl('');
                web.update();
                context.executeQueryAsync(onQuerySucceeded, onQueryFailed);
            } catch (error) {
                alert(error);
            }
        }

        function onQuerySucceeded(sender, args) {
            alert("The branding of your site was succesfully changed");
            window.location = window.location.href;
        }

        function onQueryFailed(sender, args) {
            alert('Request failed. ' + args.get_message() +
                '\n' + args.get_stackTrace());
        }

    </script>
    <div class="ms-hide">

Below the <webpartpages:webpartzone control, place the HTML code for the buttons:

    <div class="ms-hide">
        <webpartpages:webpartzone runat="server" title="loc:TitleBar" id="TitleBar" allowlayoutchange="false" allowpersonalization="false" style="display: none;" />
    </div>
    <br>
    <br>
    <a href="JavaScript:branding();" id="brand-button">Brand this site</a>
    <br />
    <a href="JavaScript:unBranding();"  id="unBrand-button">Unbrand this site</a>
    <table class="ms-core-tableNoSpace ms-webpartPage-root" width="100%">

Finally, just before the end of the ContentPlaceHolder PlaceHolderMain section paste the code for disabling the button that is useless depending on the site is branded or not:

    </table>
        <script type="text/javascript">
            var logoUrl =$(".ms-siteicon-img").attr('src');
      
            if (logoUrl.indexOf('Contoso') > -1) {
                document.getElementById('brand-button').disabled = "disabled";
                document.getElementById('brand-button').style.color = "silver";
            } else {
                document.getElementById('unBrand-button').disabled = "disabled";
                document.getElementById('unBrand-button').style.color = "silver";
            }
         </script>
</asp:Content>

2.6 Testing the page
After having copied the previous code at the right places within the Web Part page and well replaced the paths with the ones corresponding to the right locations of your environment you should obtain this scenario:
When you land on the page for the first time the site is not branded and the "Unbrand this Site" link-button is disabled:



If you click on the "Brand" button, you are soon warned that the customizations were successfully performed:



Then, when you close the Javascript alert pop-up, you are relocated onto the same page, but with the new site branding! And you can also see that the "Brand" button has been disabled while the "UnBrand" button has been enabled.



3 - Aknowledgements