A scope is a context in which tracking and catalog export occur. A scope contains an instance of an Optimizely Product Recommendations Engine and one or more websites that are communicating with that instance. Scopes are always mutually exclusive and never nested; a tracking action occurs only in a single scope. Recommendations for products belonging to a specific scope are not given for tracking actions occurring in another scope.
A scope alias (referred to as “alias” from now on) is a shorthand version of the scope's name. Aliases are used as suffixes to appSettings key attribute values. The purpose of an alias is to improve the readability of configuration settings. Aliases are used in configuration settings only; they are not used in the API.
Settings that do not use an alias suffix are called scopeless. In earlier versions of the configuration schema, all settings are scopeless. Scopeless settings are used as a fallback if a requested setting is not defined for a specific scope. This makes the new configuration schema backwards-compatible.
If asked for a valid setting for scope X, and scope X is not defined in the configuration file, the internal configuration class falls back to the corresponding scopeless setting.
The new version of Recommendations (Personalization) introduces a set of appSettings that let you configure several scopes within the same configuration file. The new version is backwards compatible with the old configuration scheme. So, if you do not want to use the scope feature, do not update your configuration when upgrading.
ScopeAliasMapping [New in Personalization.Common 2.0, the Commerce native integration package for Personalization]
ScopeAliasMapping is the only new setting in version 2.0, but because aliases are appended as a suffix to the key attribute value, you may have to modify existing keys. See Installing and configuring the native integration package for the list of other settings.
To define scopes that you will use, add a ScopeAliasMapping setting for each scope:
<add key="episerver:personalization.ScopeAliasMapping.[Alias]" value="[Scope]"/>
The value attribute contains the scope name. The alias should be a shorthand name to make it easier to read in the configuration file. Note that the alias value is specified as a suffix in the key attribute. When extracting the alias from the key attribute, episerver:personalization.ScopeAliasMapping. is trimmed off the beginning of the key, and the remainder is the alias. In the example above, [Alias] is the alias. Any string can be the scope and alias values as long as they do not include reserved XML characters.
Together, the ScopeAliasMappings act as the master list of scopes. The aliases defined there are used to find other settings that apply to that scope. Any Personalization appSetting keys with an alias not defined as a ScopeAliasMapping are ignored.
Each ScopeAliasMapping adds a requirement that all other, required Personalization appSettings are defined using the same alias suffix. This is validated upon site initialization; an exception is thrown if any required settings are missing.
You can configure more than one channel per scope. So, a scope can have multiple channels per tracking request.
To configure multiple channels, use the ScopeAliasMapping setting and add a channel setting for each channel. For example:
<add key="episerver:personalization.ClientToken.[Alias].[Channel]" value="[ChannelToken]"/>
- [Channel] is the name of channel used for tracking (for example: web, mobile).
- [Channel] is specified as a suffix in the key attribute. When extracting the channel from the key attribute, episerver:personalization.ClientToken.Alias. is trimmed from the beginning of the key, and the remainder is the channel.
- The value attribute is the ChannelToken value.
In the example above, [Channel] is the channel’s name. Any string can be the channel’s name and token values, but they cannot include reserved XML characters.
A Personalization appSetting is either required, optional, or global. The following rules apply to each group.
- Required – Treated as a group; all of them must be defined for each scope. If there are no scopes, all must be defined as scopeless. Scopeless settings are allowed in parallel with scoped settings and are, in that case, used for all undefined scopes. See Fallback.
- Optional – May be defined on the scope level, scopeless level, or not at all.
- Global – Settings cannot be defined per scope.
The fallback scheme that works on the following levels:
- Scope fallback – At runtime, if asked to provide settings for an unknown scope (that is, a scope that has no matching ScopeAliasMapping), the fallback returns the scopeless settings if available.
- Channel fallback [New in Commerce 13.8.0 and Personalization.Common v3.1.0] – If, at runtime, you provide settings for an unknown channel (that is, a channel that does not match any channel’s name), the default channel of web is returned.
- Optional settings fallback – If an optional setting is not defined for a scope, the fallback uses the scopeless setting and, finally, the hard-coded default value.
Individual required settings do not allow for fallback due to rules that apply to them. You cannot define global settings per scope. Defining a global setting on the scope level does not cause an error, but the scoped value is ignored, instead falling back to the scopeless or default value.
Consider the following partial configuration. [V1 and V2 keys are new in Commerce 13.8.0 and Personalization.Common v3.1.0]
<add key="episerver:personalization.ScopeAliasMapping.Alias1" value="Scope1"/>
<add key="episerver:personalization.BaseApiUrl.Alias1" value="A"/>
<add key="episerver:personalization.Site.Alias1" value="B" />
<add key="episerver:personalization.ClientToken.Alias1" value="C" />
<add key="episerver:personalization.ClientToken.Alias1.Web" value="V1" />
<add key="episerver:personalization.ClientToken.Alias1.Mobile" value="V2" />
<add key="episerver:personalization.BaseApiUrl" value="D" />
<add key="episerver:personalization.Site" value="E" />
<add key="episerver:personalization.ClientToken" value="F" />
<add key="episerver:personalization.CatalogNameForFeed" value="G"/>
At runtime, the Site value (required) is B for Scope1, and E for all other scopes. The CatalogNameForFeed value (optional) is G for all scopes. In Commerce versions 13.8.0 and Personalization.Common v3.1.0 and up, the channel token value is V1 for web channel, and V2 for mobile channel.
The EpiServer.Personalization package provides a default implementation of the Scope feature. This implementation assumes that each CMS site uses one Commerce catalog and one Product Recommendations Engine instance. If this setup fits your needs, you can set it up using the configuration only. If you need a more specialized setup, write custom code.
If you have only one site that uses Personalization, use scopeless settings only; do not bother to define scopes.
If you need to configure several scopes, the scope names are expected to be each site's SiteDefinitionID. You can find this value in the CMS Admin view > Config tab > Manage Websites. Starting with EPiServer.CMS.UI version 11.5.0, this value is shown in a field for each site.
In earlier versions, get the ID from the query string in the links in the list of websites. Open each link in its own browser tab and copy the ID from the address field.
The default implementation iterates over all scopes registered in configuration and exports one full catalog feed per scope. Also, you can write custom code to filter each exported product.
The default implementation stores CUID and SessionID in cookies, as it did in earlier versions. If you have a single site using Personalization, you do not need to modify this behavior. To add additional scopes, you probably need to implement your own ICookieService. See Modifying the Default Behavior for further discussion.
The scope is automatically set to the SiteDefinitionID of the site that triggers the tracking action. This value is used to load the correct settings from configuration. You can override this behavior by passing the scope to the tracking method yourself. See Specifying scope for tracking actions.
If your installation differs from what is supported by the default implementation, you need to write custom code to fit your purposes.
There are several interfaces you can create custom implementations of to modify the behavior of the catalog export. The main change in the scope feature release is that the scope name for the current export is passed to all extension points. The following interfaces are of extra interest if you want to use fractions of the same catalog for different scopes
- ICatalogItemFilter. Lets you decide whether to include each item in the export for a given scope.
- IEntryUrlService/IFeedUrlConverter. Lets you define the absolute URL for a product, based on a given scope.
See the SDK for the full list of extension points.
CUIDs and SessionIDs are created by the Product Recommendations Engine. They are only valid for the Product Recommendations Engine instance that created them. This means that these values need to be siloed per scope. The default implementation stores CUID and SessionID in cookies that are bound to the current domain. This implementation covers the common scenario where no domain is shared between scopes.
If you want to support a scenario where two different scopes share a domain, write custom code to support that.
If you want to split an existing scope in two, make sure that the values in existing cookies are not used for the new scope. To control how cookies are created and read for a scope, replace the default ICookieService (namespace EPiServer.Personalization.Common) implementation with your custom implementation.
If the correct scope cannot be derived from the current SiteDefinitionID, you are responsible for determining the correct scope for each tracking action. The EPiServer.Tracking.Commerce Nuget package contains extension methods for TrackingService that let you to pass scope to the Track method.
The following naive scope calculation implementation is based on one of the RecommendationService class methods in Quicksilver.
public async Task<TrackingResponseData> TrackCategoryAsync(HttpContextBase httpContext, NodeContent category)
if (_contextModeResolver.CurrentMode != ContextMode.Default)
var trackingData = _trackingDataFactory.CreateCategoryTrackingData(category, httpContext);
var scope = category.Name.StartsWith("Mens") ? "MensFashion" : "WomensFashion";
return await _trackingService.TrackAsync(trackingData, httpContext, _contentRouteHelperAccessor().Content, scope);
Updated 10 months ago