Here are the steps,
Step 1: Create Provider Hosted App using Visual Studio as in the following,
Go to Visual Studio, Office/SharePoint Tab, Apps and select App for SharePoint. Give some unique name “SharePointBranding”.
Select Hosting option as “Provider Hosted” as in the following,
Select Web Application type “ASP.NET Web Form Application” as in the following screenshot,
Select Authentication Type as “Certificate” as in the following,:
Step 2: Solution Structure looks like the following,
- Add folder named “CustomJS” with new custom JavaScript file named “App.js”.
-
- function getQueryStringParameter(urlParameterKey)
- {
- var params = document.URL.split('?')[1].split('&');
- var strParams = '';
- for (var i = 0; i < params.length; i = i + 1)
- {
- var singleParam = params[i].split('=');
- if (singleParam[0] == urlParameterKey) return singleParam[1];
- }
- }
-
- function Branding()
- {
- console.log("Branding");
- }
- $(document).ready(function ()
- {
-
- var spHostUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
-
- var layoutsRoot = spHostUrl + '/_layouts/15/';
-
- $.getScript(layoutsRoot + 'SP.Runtime.js', function ()
- {
- $.getScript(layoutsRoot + 'SP.js', function ()
- {
-
-
- $.getScript(layoutsRoot + 'SP.UI.Controls.js', renderSPChrome);
- });
- });
- });
- Add folder named “Resources” with two files “NewCustom.spcolor” and “NewCustombg.jpg”
Step 3: Default.aspx page
- <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SharePointBrandingWeb.Default" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml">
-
- <head runat="server">
- <title></title>
- <script src="../Scripts/jquery-1.9.1.min.js" type="text/javascript"></script>
- </head>
-
- <body>
- <form id="form1" runat="server">
- <asp:ScriptManager ID="ScriptManager1" runat="server" EnableCdn="True" />
- <div id="divSPChrome"></div>
- <div style="left: 40px; position: absolute;">
- <table>
- <tr>
- <td>Set Out Of Box theme to host site </td>
- <td>
- <asp:DropDownList runat="server" ID="drpThemes" Width="100px">
- <asp:ListItem Text="Orange" Value="Orange" Selected="True"></asp:ListItem>
- <asp:ListItem Text="Green" Value="Green"></asp:ListItem>
- <asp:ListItem Text="Nature" Value="Nature"></asp:ListItem>
- <asp:ListItem Text="Blossom" Value="Blossom"></asp:ListItem>
- <asp:ListItem Text="Office" Value="Office"></asp:ListItem>
- <asp:ListItem Text="Breeze" Value="Breeze"></asp:ListItem>
- <asp:ListItem Text="Immerse" Value="Immerse"></asp:ListItem>
- </asp:DropDownList>
- </td>
- <td>
- <asp:Button runat="server" ID="btnSetThemeForHost" Text="Set out of the box theme" OnClick="btnSetThemeForHost_Click" /> </td>
- </tr>
- <tr>
- <td colspan="3"> </td>
- </tr>
- <tr>
- <td colspan="3">
- <asp:Label ID="lblStatus1" runat="server" /> </td>
- </tr>
- </table>
- <br />
- <table>
- <tr>
- <td>Deploy a new theme and apply to host site </td>
- <td colspan="2">
- <asp:Button runat="server" ID="btnDeployTheme" Text="Deploy a custom theme" OnClick="btnDeployTheme_Click" /> </td>
- </tr>
- <tr>
- <td colspan="3"> </td>
- </tr>
- <tr>
- <td colspan="3">
- <asp:Label ID="lblStatus2" runat="server" /> </td>
- </tr>
- </table>
- </div>
- <script src="../CustomJS/App.js" type="text/javascript"></script>
- </form>
- </body>
-
- </html>
Above code render UI as shown below:
Step 4: Register Script on page.
On page load we are going to register script named “
BasePageScript” into page as shown below.
- protected void Page_Load(object sender, EventArgs e)
- {
-
-
- var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
- using(var clientContext = spContext.CreateUserClientContextForSPHost())
- {
- clientContext.Load(clientContext.Web, web => web.Title);
- clientContext.ExecuteQuery();
- Response.Write(clientContext.Web.Title);
- }
-
- string script = @ "
-
- function chromeLoaded()
- {
- $('body').show();
- }
-
- function renderSPChrome()
- {
-
- var options = {
- 'appTitle': document.title,
- 'onCssLoaded': 'chromeLoaded()'
- };
- console.log('renderSPChrome ...');
-
- var chromeNavigation = new SP.UI.Controls.Navigation('divSPChrome', options);
- chromeNavigation.setVisible(true);
- }
- ";
-
- Page.ClientScript.RegisterClientScriptBlock(typeof (Default), "BasePageScript", script, true);
- }
Step 5: Apply Existing Theme to SharePoint Site as follows:
- Select any theme from dropdown menu and click on button “Set out Of Box Theme”.
- On button click “btnSetThemeForHost_Click()” method get called, which in turn call “SetThemeBasedOnName()” method.
- public void SetThemeBasedOnName(Web web, string themeName)
- {
-
- List themeList = web.GetCatalog(124);
- web.Context.Load(themeList);
- web.Context.ExecuteQuery();
-
- CamlQuery query = new CamlQuery();
- string camlString = @ " < View > < Query > < Where > < Eq > < FieldRef Name = 'Name' / > < Value Type = 'Text' >
- {
- 0
- } < /Value> < /Eq> < /Where> < /Query> < /View>";
-
- camlString = string.Format(camlString, themeName);
- query.ViewXml = camlString;
- var found = themeList.GetItems(query);
- web.Context.Load(found);
- web.Context.ExecuteQuery();
- if (found.Count > 0)
- {
- Microsoft.SharePoint.Client.ListItem themeEntry = found[0];
-
- string spColorURL = null;
- if (themeEntry["ThemeUrl"] != null && themeEntry["ThemeUrl"].ToString().Length > 0)
- {
- spColorURL = MakeAsRelativeUrl((themeEntry["ThemeUrl"] as FieldUrlValue).Url);
- }
- string spFontURL = null;
- if (themeEntry["FontSchemeUrl"] != null && themeEntry["FontSchemeUrl"].ToString().Length > 0)
- {
- spFontURL = MakeAsRelativeUrl((themeEntry["FontSchemeUrl"] as FieldUrlValue).Url);
- }
- string backGroundImage = null;
- if (themeEntry["ImageUrl"] != null && themeEntry["ImageUrl"].ToString().Length > 0)
- {
- backGroundImage = MakeAsRelativeUrl((themeEntry["ImageUrl"] as FieldUrlValue).Url);
- }
-
- web.ApplyTheme(spColorURL, spFontURL, backGroundImage, false);
-
- if (themeEntry["MasterPageUrl"] != null && themeEntry["MasterPageUrl"].ToString().Length > 0)
- {
- web.MasterUrl = MakeAsRelativeUrl((themeEntry["MasterPageUrl"] as FieldUrlValue).Url);
- }
-
- web.Context.ExecuteQuery();
- }
- }
- private string MakeAsRelativeUrl(string urlToProcess)
- {
- Uri uri = new Uri(urlToProcess);
- return uri.AbsolutePath;
- }
- protected void btnSetThemeForHost_Click(object sender, EventArgs e)
- {
- var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
- using(var clientContext = spContext.CreateUserClientContextForSPHost())
- {
- if (clientContext != null)
- {
- Web web = clientContext.Web;
- string selectedTheme = drpThemes.SelectedValue;
- SetThemeBasedOnName(web, selectedTheme);
- lblStatus1.Text = string.Format("Theme '{0}' has been applied to the <a href='{1}'>host web</a>.", selectedTheme, spContext.SPHostUrl.ToString());
- }
- }
- }
- Existing theme resides inside ‘Composite Look Gallery’, so we first need to get access to composite look gallery for that the following line of code is used.
- List themeList = web.GetCatalog(124);
-
- web.Context.Load(themeList);
-
- web.Context.ExecuteQuery();
- Using CAML Query object, get list item corresponding to selected theme.
- Let’s find “ThemeURL”, “ImageURL”, “MasterPageURL” which is needed to apply theme to the site.
-
-
- web.ApplyTheme(spColorURL, spFontURL, backGroundImage, false);
Step 6: Output of applying existing theme to SharePoint Site.
Step 7: Deploy Custom Theme
- In order to create custom theme, we need some resources like SPColor and Image to set background for site.
- Click on “Deploy Custom Theme”, it call “btnDeployTheme_Click()” method.
- protected void btnDeployTheme_Click(object sender, EventArgs e)
- {
- var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
- using(var clientContext = spContext.CreateUserClientContextForSPHost())
- {
- if (clientContext != null)
- {
- Web web = clientContext.Web;
- string CustomColor = HostingEnvironment.MapPath(string.Format("~/{0}", "Resources/NewCustom.spcolor"));
- string CustomBackground = HostingEnvironment.MapPath(string.Format("~/{0}", "Resources/NewCustombg.jpg"));
-
- DeployContosoThemeToWeb(web, "NewCustomTheme", CustomColor, string.Empty, CustomBackground, string.Empty);
-
- SetThemeBasedOnName(web, "NewCustomTheme");
- lblStatus2.Text = string.Format("Custom theme called 'NewCustomTheme' has been uploaded and applied to the <a href='{0}'>host web</a>.", spContext.SPHostUrl.ToString());
- }
- }
- }
Theme Gallery gets modified, two new entry get added here as shown below for color and background image.
- New Theme getting deploy into "Composite Look Gallery" as shown below.
Step 8: Source Code
- using Microsoft.SharePoint.Client;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Web;
- using System.Web.Hosting;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- namespace SharePointBrandingWeb
- {
- public partial class Default: System.Web.UI.Page
- {
- protected void Page_PreInit(object sender, EventArgs e)
- {
- Uri redirectUrl;
- switch (SharePointContextProvider.CheckRedirectionStatus(Context, out redirectUrl))
- {
- case RedirectionStatus.Ok:
- return;
- case RedirectionStatus.ShouldRedirect:
- Response.Redirect(redirectUrl.AbsoluteUri, endResponse: true);
- break;
- case RedirectionStatus.CanNotRedirect:
- Response.Write("An error occurred while processing your request.");
- Response.End();
- break;
- }
- }
- protected void Page_Load(object sender, EventArgs e)
- {
-
-
- var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
- using(var clientContext = spContext.CreateUserClientContextForSPHost())
- {
- clientContext.Load(clientContext.Web, web => web.Title);
- clientContext.ExecuteQuery();
- Response.Write(clientContext.Web.Title);
- }
-
- string script = @ "
-
- function chromeLoaded()
- {
- $('body').show();
- }
-
- function renderSPChrome()
- {
-
- var options = {
- 'appTitle': document.title,
- 'onCssLoaded': 'chromeLoaded()'
- };
- console.log('renderSPChrome ...');
-
- var chromeNavigation = new SP.UI.Controls.Navigation('divSPChrome', options);
- chromeNavigation.setVisible(true);
- }
- ";
-
- Page.ClientScript.RegisterClientScriptBlock(typeof (Default), "BasePageScript", script, true);
- }
- public void SetThemeBasedOnName(Web web, string themeName)
- {
-
- List themeList = web.GetCatalog(124);
- web.Context.Load(themeList);
- web.Context.ExecuteQuery();
-
- CamlQuery query = new CamlQuery();
- string camlString = @ " < View > < Query > < Where > < Eq > < FieldRef Name = 'Name' / > < Value Type = 'Text' >
- {
- 0
- } < /Value> < /Eq> < /Where> < /Query> < /View>";
-
- camlString = string.Format(camlString, themeName);
- query.ViewXml = camlString;
- var found = themeList.GetItems(query);
- web.Context.Load(found);
- web.Context.ExecuteQuery();
- if (found.Count > 0)
- {
- Microsoft.SharePoint.Client.ListItem themeEntry = found[0];
-
- string spColorURL = null;
- if (themeEntry["ThemeUrl"] != null && themeEntry["ThemeUrl"].ToString().Length > 0)
- {
- spColorURL = MakeAsRelativeUrl((themeEntry["ThemeUrl"] as FieldUrlValue).Url);
- }
- string spFontURL = null;
- if (themeEntry["FontSchemeUrl"] != null && themeEntry["FontSchemeUrl"].ToString().Length > 0)
- {
- spFontURL = MakeAsRelativeUrl((themeEntry["FontSchemeUrl"] as FieldUrlValue).Url);
- }
- string backGroundImage = null;
- if (themeEntry["ImageUrl"] != null && themeEntry["ImageUrl"].ToString().Length > 0)
- {
- backGroundImage = MakeAsRelativeUrl((themeEntry["ImageUrl"] as FieldUrlValue).Url);
- }
-
- web.ApplyTheme(spColorURL, spFontURL, backGroundImage, false);
-
- if (themeEntry["MasterPageUrl"] != null && themeEntry["MasterPageUrl"].ToString().Length > 0)
- {
- web.MasterUrl = MakeAsRelativeUrl((themeEntry["MasterPageUrl"] as FieldUrlValue).Url);
- }
-
- web.Context.ExecuteQuery();
- }
- }
- private string MakeAsRelativeUrl(string urlToProcess)
- {
- Uri uri = new Uri(urlToProcess);
- return uri.AbsolutePath;
- }
- protected void btnSetThemeForHost_Click(object sender, EventArgs e)
- {
- var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
- using(var clientContext = spContext.CreateUserClientContextForSPHost())
- {
- if (clientContext != null)
- {
- Web web = clientContext.Web;
- string selectedTheme = drpThemes.SelectedValue;
- SetThemeBasedOnName(web, selectedTheme);
- lblStatus1.Text = string.Format("Theme '{0}' has been applied to the <a href='{1}'>host web</a>.", selectedTheme, spContext.SPHostUrl.ToString());
- }
- }
- }
- protected void btnDeployTheme_Click(object sender, EventArgs e)
- {
- var spContext = SharePointContextProvider.Current.GetSharePointContext(Context);
- using(var clientContext = spContext.CreateUserClientContextForSPHost())
- {
- if (clientContext != null)
- {
- Web web = clientContext.Web;
- string CustomColor = HostingEnvironment.MapPath(string.Format("~/{0}", "Resources/NewCustom.spcolor"));
- string CustomBackground = HostingEnvironment.MapPath(string.Format("~/{0}", "Resources/NewCustombg.jpg"));
-
- DeployContosoThemeToWeb(web, "NewCustomTheme", CustomColor, string.Empty, CustomBackground, string.Empty);
-
- SetThemeBasedOnName(web, "NewCustomTheme");
- lblStatus2.Text = string.Format("Custom theme called 'NewCustomTheme' has been uploaded and applied to the <a href='{0}'>host web</a>.", spContext.SPHostUrl.ToString());
- }
- }
- }
- public void DeployContosoThemeToWeb(Web web, string themeName, string colorFilePath, string fontFilePath, string backgroundImagePath, string masterPageName)
- {
-
- if (!string.IsNullOrEmpty(colorFilePath))
- {
- DeployFileToThemeFolderSite(web, colorFilePath);
- }
- if (!string.IsNullOrEmpty(fontFilePath))
- {
- DeployFileToThemeFolderSite(web, fontFilePath);
- }
- if (!string.IsNullOrEmpty(backgroundImagePath))
- {
- DeployFileToThemeFolderSite(web, backgroundImagePath);
- }
-
- AddNewThemeOptionToSite(web, themeName, colorFilePath, fontFilePath, backgroundImagePath, masterPageName);
- }
- private void DeployFileToThemeFolderSite(Web web, string sourceAddress)
- {
-
- string file = sourceAddress;
-
- List themesList = web.GetCatalog(123);
- web.Context.Load(themesList);
- web.Context.ExecuteQuery();
- Folder rootfolder = themesList.RootFolder;
- web.Context.Load(rootfolder);
- web.Context.Load(rootfolder.Folders);
- web.Context.ExecuteQuery();
- Folder folder15 = rootfolder;
- foreach(Folder folder in rootfolder.Folders)
- {
- if (folder.Name == "15")
- {
- folder15 = folder;
- break;
- }
- }
-
- FileCreationInformation newFile = new FileCreationInformation();
- newFile.Content = System.IO.File.ReadAllBytes(file);
- newFile.Url = folder15.ServerRelativeUrl + "/" + System.IO.Path.GetFileName(sourceAddress);
- newFile.Overwrite = true;
- Microsoft.SharePoint.Client.File uploadFile = folder15.Files.Add(newFile);
- web.Context.Load(uploadFile);
- web.Context.ExecuteQuery();
- }
- private void AddNewThemeOptionToSite(Web web, string themeName, string colorFilePath, string fontFilePath, string backGroundPath, string masterPageName)
- {
-
- List themesOverviewList = web.GetCatalog(124);
- web.Context.Load(themesOverviewList);
- web.Context.ExecuteQuery();
-
- if (!ThemeEntryExists(web, themesOverviewList, themeName))
- {
-
- if (!web.IsObjectPropertyInstantiated("ServerRelativeUrl"))
- {
- web.Context.Load(web);
- web.Context.ExecuteQuery();
- }
-
- ListItemCreationInformation itemInfo = new ListItemCreationInformation();
- Microsoft.SharePoint.Client.ListItem item = themesOverviewList.AddItem(itemInfo);
- item["Name"] = themeName;
- item["Title"] = themeName;
- if (!string.IsNullOrEmpty(colorFilePath))
- {
- item["ThemeUrl"] = URLCombine(web.ServerRelativeUrl, string.Format("/_catalogs/theme/15/{0}", System.IO.Path.GetFileName(colorFilePath)));
- }
- if (!string.IsNullOrEmpty(fontFilePath))
- {
- item["FontSchemeUrl"] = URLCombine(web.ServerRelativeUrl, string.Format("/_catalogs/theme/15/{0}", System.IO.Path.GetFileName(fontFilePath)));
- }
- if (!string.IsNullOrEmpty(backGroundPath))
- {
- item["ImageUrl"] = URLCombine(web.ServerRelativeUrl, string.Format("/_catalogs/theme/15/{0}", System.IO.Path.GetFileName(backGroundPath)));
- }
-
- if (string.IsNullOrEmpty(masterPageName))
- {
- item["MasterPageUrl"] = URLCombine(web.ServerRelativeUrl, "/_catalogs/masterpage/seattle.master");
- }
- else
- {
- item["MasterPageUrl"] = URLCombine(web.ServerRelativeUrl, string.Format("/_catalogs/masterpage/{0}", Path.GetFileName(masterPageName)));
- }
- item["DisplayOrder"] = 11;
- item.Update();
- web.Context.ExecuteQuery();
- }
- }
- private bool ThemeEntryExists(Web web, List themeList, string themeName)
- {
- CamlQuery query = new CamlQuery();
- string camlString = @ " < View > < Query > < Where > < Eq > < FieldRef Name = 'Name' / > < Value Type = 'Text' >
- {
- 0
- } < /Value> < /Eq> < /Where> < /Query> < /View>";
-
- camlString = string.Format(camlString, themeName);
- query.ViewXml = camlString;
- var found = themeList.GetItems(query);
- web.Context.Load(found);
- web.Context.ExecuteQuery();
- if (found.Count > 0)
- {
- return true;
- }
- return false;
- }
- private string URLCombine(string baseUrl, string relativeUrl)
- {
- if (baseUrl.Length == 0) return relativeUrl;
- if (relativeUrl.Length == 0) return baseUrl;
- return string.Format("{0}/{1}", baseUrl.TrimEnd(new char[]
- {
- '/',
- '\\'
- }), relativeUrl.TrimStart(new char[]
- {
- '/',
- '\\'
- }));
- }
- }
- }
Step 9: Final Output