Developing for Multiple Environments in SharePoint Online

Published 11/15/2018 04:09 PM   |    Updated 05/16/2019 11:27 AM
The transition from developing for SharePoint on premises to SharePoint Online contains many hurdles that need to be overcome: switching to client-side object model, implementing custom branding without custom master pages and writing web parts in JavaScript with the removal of managed code, just to name a few. However, one of the major hurdles is writing code that can isolate a SharePoint Online site collection as its own environment due to architectural differences with the on-premise version.


  • On-premise vs. online architecture
  • Environment identification 
  • Using shared service applications for custom functionality
  • Querying lists and search service

On-premise vs. online architecture


The normal practice for a SharePoint On-Premises farm is to have different servers for the various environments: development, Quality Assurance (QA) and production. Each of these environments resides on a different server and is independent. This means each has its own set of service applications developers can leverage (search, user profiles, managed metadata, etc.).

Based on this architecture, developers can make more assumptions about where a site collection will reside, which normally is at the root of a web application. Each environment will have at least one web application with a unique host URL to distinguish it from another. With this isolation, the information on one environment will not appear in another environment. The image below illustrates the environment separation.

Figure B: SharePoint Online environments

Environment identification in SharePoint Online


Sometimes, a solution needs to know what environment it’s currently running in so it can adjust functionality, such as turning off caching in non-roduction environments. Since the host name is no longer unique between environments, there needs to be another way to determine where the code is running.

A way to achieve this is to dynamically change the contents of the code before it’s provisioned to a site. Using remote provisioning allows for this file manipulation. The example below shows how to use a .NET website with a web.config transforms on an AppSetting to perform this action.

Setting up web.config transforms


Visual Studio has a built-in web.config transformer that allows for manipulation of the web.config file when it’s published under the different configurations. The default configurations of Debug and Release will be used for this example. The current configuration can be changed via the drop-down in the Visual Studio toolbar.

Figure C: Visual Studio Configuration drop-down 

Below are the XML entries that will reside in their respective config file. In this case, the “value” attribute of the appSettings with the key of “Environment” will be adjusted based on the Visual Studio configuration selected.

Figure D: Visual Studio web config with transformations

Web.config

<configuration>
<appSettings>
    	<add key=“Environment” value=“DEV” />
  </appSettings>
</configuration>

Web.Debug.config

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="Environment" value="QA" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
  </appSettings>
</configuration>

Web.Release.Config

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="Environment" value="PROD" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
  </appSettings>
</configuration>

Manipulation of JavaScript file


Once these web.config manipulations are in place, the code that provisions files to the SharePoint site can be used to modify the contents of a JavaScript file to create an environment JSON object (CSGEnvironment) that can be used by code written for the site.

JavaScript file before modification

var CSGEnvironment = {
    Environment: "{ENVIRONMENT}",
};

JavaScript file after modification for production deployment 

var CSGEnvironment = {
    Environment: "PROD",
};

C# code to modify file 

private static System.IO.Stream ReplaceFileTokens(string filePath) 
{
            	string fileText = System.IO.File.ReadAllText(filePath);
            	
string currentEnvironment = WebConfigurationManager.AppSettings["Environment"];
fileText = fileText.Replace("{ENVIRONMENT}",  "PROD");  

              	byte[] bytes = System.Text.Encoding.ASCII.GetBytes(fileText);
            	System.IO.Stream ms = new System.IO.MemoryStream(bytes);
            	return ms;


Using shared service applications for custom functionality


As previously called out, all environments will share the SharePoint services on a SharePoint Online tenant. This section will discuss approaches that can be taken to keep your environments separated inside those shared services.

Term Store/User Profile services


Often, a developer will use the Term Store and User Profile services as part of custom functionality. Common use cases would be cross-site collection navigation using a term set in the Term Store or user personalization on the site using a custom user profile property to store configurations.

When the development is done on premises, the same name can be used across environments for these items due to the isolation of the service applications between environments (see Figure A). In SharePoint Online, this is not the case since these service applications are shared (see Figure A), which means term sets and user profile properties should be created for each environment with different names.

To make sure the code is accessing the proper items in the services, the same environment identifier approach with web.config transforms, as outlined above, can be used to specify the correct name at deployment time.

JavaScript file Before Modification

var CSGEnvironment = {
    Environment: "{ENVIRONMENT}",
    TermSetGlobalNav: "{TERM_SET_GLOBAL_NAV}",
    UserProfilePersonalize : "{USER_PROFILE_PERSONAL}"
}; 

JavaScript file after modification for production deployment 

var CSGEnvironment = {
    Environment: "PROD",
    TermSetGlobalNav: "CSGGlobalNav-Prod",
    UserProfilePersonalize : "CSGPersonalize-Prod"
};

Querying lists and search service


When retrieving data from lists and search, developers need to actively specify where they would like the data to be pulled from. This section will provide some recommendations on how this can be accomplished.

Querying lists


Due to each environment being a separate site collection, it’s important to make sure list querying being done is at the correct scope. When using the REST API to get list data, the request URL can dynamically be adjusted prior to the REST call being made. The JavaScript code snippets below show how this can be done to generate a call at the host, site collection and web levels.

var restURLHost = getHostUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title”
//restURLHost: https://company.sharepoint.com

var restURLSiteCollection = getSiteCollectionUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title"
//restURLSiteCollection: https://company.sharepoint.com/sites/SiteCollectionName

var restURLWeb = getWebUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title"
//restURLSiteCollection: https://company.sharepoint.com/sites/SiteCollection/Subsite



function getHostUrl() {
    return window.location.protocol + "//" + window.location.host;
};

function getSiteCollectionUrl() {
    return getHostUrl() + _spPageContextInfo.siteServerRelativeUrl;

};
function getWebUrl() {
    return getHostUrl() + _spPageContextInfo.webServerRelativeUrl;
}; 

Search service


Since SharePoint Online only contains one search service, the data for all environments will reside in a single search index. Search is a powerful tool inside SharePoint to get data across site collections and sites. To make sure the proper data is being retrieved, the “Path” managed property can be used in the query to guarantee search returns the data from the desired sites. The syntax being used for the example is Keyword Query Language (KQL). The following query condition will only return data from the development site collection (/sites/dev).

Path:https://company.sharepoint.com/sites/dev

For custom code using the search API to get the current environment’s URL, the same functions in the query lists section can be used to dynamically change the query.

When using out-of-the-box search web parts, such as Search Results or Content Search, SharePoint provides a “Site” token that can be placed into the query to get the URL of the current site. The web part will replace that token with the correct information prior to running the query.

Path:{Site.URL}

The screenshot below is the edit query window for the out-of-the-box Content Search web part. The query has been modified to return any document or list item on the current site that contains the word “Benefits” in the title.



Figure E: Content query web part screenshot

There are many other tokens that can be used as well. See the links in the References section at the end of this article.

Summary


Understanding the architecture of your SharePoint environment at the start of development will eliminate some of the headaches encountered the first time code gets promoted to a QA or production environment. Here are the key items to remember:

  • Modify code files at deployment time to specify the environment information.
  • SharePoint servers that were previously isolated between environments are now shared in SharePoint Online, requiring more forethought and work for the developer to make sure to display the correct data.
  • When querying lists, make sure the correct site collection or web’s URL is being used since SharePoint Online has a single host name and environments need to be site collections rather than web applications.

 

References


This article originally appeared on May 24, 2017.

Is this answer helpful?