This blog has been moved to http://blog.petersondave.com.
Check it out and let me know what you think!
With the introduction of The Experience Database (xDb) in Sitecore 7.5, MongoDB hosts the primary repository of web activity across Sitecore backed websites. Web visitors, now known as contacts, are captured along with each page view (Interactions in xDb) generated in a given browsing session. Much like its predecessor, DMS, the new xDb separates web activity by site.
When building upon xDb in a multi-site implementation, being aware of how Sitecore captures and processes this information is essential for a successful multisite configuration.
A Quick Look at Sitecore 7.5 with xDb
Contacts are identified just as they were in Sitecore DMS.
A cookie, SC_ANALYTICS_GLOBAL_COOKIE, is created with a Guid uniquely identifying the contact. From there, a contact record is created. This contact will be referenced for the lifetime of the cookie as interactions are recorded against the contact.
Site activity is captured as documents within Interactions. High level data points of Interactions include:
- Site Name
- Pages viewed with URL, item Guid
- Visit page count total
- Browser type
- Screen resolution
- Geo location data
While the structure of the data differs from DMS, as we’re now storing data as documents, commonality exists between the data points captured in DMS vs the new xDb structure.
The main takeaway with xDb is Sitecore’s ability to find matching contacts and merge those contacts given a predefined value uniquely identifying visitors. In previous versions, visitors identified by their Global Session cookie were maintained as unique visitor records with DMS. Upon processing of analytics data during the SessionEnd event in xDb, contacts are merged, creating a single consolidated view of the customer.
Contact merging is useful in two specific areas:
- Multiple browsing sessions for a single site – Such as sharing a shopping cart between a session on a PC and transferring that session to a mobile device. For more information on this approach, See Nick Wesselman’s series of in-depth Sitecore 7.5 posts.
- Multisite Sitecore Implementation – Two sites sharing the same membership model, both uniquely identifying contacts in the same way.
A Multisite Example
Suppose we have a multisite implementation, Launch Sitecore and Launch SkyNet (our fictitious Launch Sitecore evil twin). Both sites follow Sitecore’s best practice recommendations for configuration in IIS, while also sharing MongoDB, Collection and Reporting databases.
For the purpose of this example, while the two sites share membership, Single Sign-On is not implemented, requiring the user to identify themselves on both sites. Having such a setup will show how the xDb implementation handles contact merging and the importance of a common contact identification strategy shared across all sites.
Browsing Session #1: Launch Sitecore
Browsing the site for the first time results in the creation of a Global Analytics cookie. If you’re familiar with DMS, this works in the same way as previous versions. The cookie is what xDb will use to tie contacts together for unique browsing sessions.
While browsing Launch Sitecore, suppose we login, recognizing the current browsing session as a single customer within our membership model. At the point in which the user is identified, the contact, who previously was anonymous is now labelled using the unique identifier. In this example, we’re using the username from the extranet domain.
Notice how the previous page views (xDb interactions) are now tagged with the contact id of the logged in user. Launch Sitecore programmatically identifying the contact is below. The line of code we’re most interested in is Tracker.Current.Session.Identify(domainUser).
string name = Sitecore.Context.User.Profile.FullName;
if (name == String.Empty) name = Sitecore.Context.User.LocalName;
Tracker.Current.Contact.Tags.Add("Full name", name);
Tracker.Current.Contact.Identifiers.AuthenticationLevel = AuthenticationLevel.PasswordValidated;
Browsing Session #2: Launch SkyNet
Upon browsing Launch SkyNet, we have a completely different Global Session Guid in our cookie.
To Launch SkyNet, we’re anonymous and in no way connected to the user identified in Launch Sitecore. As soon as we login on Launch SkyNet, using the same logic to uniquely identify the contact (extranet domain username), Sitecore will flush the contact to xDb, updating the interactions with the contact id of the recognized user.
Any updates to facets, tags, counters, automation states, and contact attributes will be auto-merged within the MergeContacts pipeline processors.
Key takeaway: Contact consolidation occurs at the point in which the current tracking session is identified via Tracker.Current.Session.Identify().
Regardless of how many sites you have running through a single instance of Sitecore, xDb processing and contact merging will consolidate contacts while maintaining the page interactions of each site. It is through this process we’re able to maintain a single view of the customer and maintain the customer lifetime value as seen by the Sitecore experience database.
Risks of splitting separate Sitecore instances to separate instances of xDb processing will result in a partial view of the customer and their relative engagement value for each site instance.
With the release of the Sitecore 8 MVP Technical Preview, many of the features showcased during Sitecore Symposium 2014 were made available for review. The focus of this post is to detail high-level changes between Sitecore 7.5 and the technical preview of Sitecore 8.
Many new components were delivered in the new version of Sitecore, as well as, renaming of existing features and packaging of popular modules used in pre-Sitecore 8 versions.
- Page Editor = Experience Editor
- Marketing Center = Marketing Control Panel
- Email Campaign Manager = Email Experience Manager
- Experience Analytics
- Experience Profile
- Experience Optimization
- List Manager
- Path Analyzer
- App Center
- Executive Dashboard
- Versioned Layouts
- Web API Services (SPEAK components and building applications dependent upon Sitecore data)
Existing Features Packaged with Sitecore 8
- Federated Experience Manager (available in pre Sitecore 8 versions)
- Social (previously Sitecore Social Connected)
General Look and Feel
The look and feel of the client is much improved. After the initial load, performance feels much quicker than Sitecore 7.5 and previous versions. The Launch Pad icon at the top of the page comes in very handy when wanting to switch between the new Sitecore 8 components and content editing, or experience editing; features that you’re already used to using in pre-8 installations.
Moving through the various steps of editing content, publishing, workflow, etc. match that of previous versions — just in a different layout. Everything is pretty much where you would expect it, but with a cleaner look and feel.
Obviously, with the addition of new Sitecore 8 features, you’ll rely heavily on the Launch Pad to access these components.
Sitecore Client Enhancements
The experience editor is accessible from the Sitecore Experience Platform, the start menu or directly within the content editor ribbon. Just as before, the only difference being the rename of Page Editor to Experience Editor.
Content editors can establish tests, set goals and review performance reports.
Any tests created through Experience Optimization are saved under the Marketing Center Test Lab item buckets.
An example path for a home page test:
- Item: /sitecore/system/Marketing Center/Test Lab/2014/10/01/01/11/Home 20141001T011146Z
- Template: /sitecore/templates/System/Analytics/Testing/Test Definition
The Experience Optimization dashboard exposes reports enhancing new gamification concepts to the content editing and testing experience:
When moving content changes through Workflow, new “Approve with Test” and “Approve without Test” are available, consistently reminding content editors and decision makers to consider A/B testing at a content level.
Email Experience Manager
The layout of the email campaign manager has changed, allowing for message creation and list importing available from the same menu
The List manager allows for creation of lists from files, or manual entry from an empty list
New to Sitecore 8, the Experience Analytics feature brings together multivariate testing results, engagement scoring and overall site tracking statistics together in one location. The result is a powerful, new array of reports coupled with date range filtering and reporting facets.
One of the new features I’ve had the most fun with so far is the Path Analyzer. Clicking on the map, zooming in and out, selecting successful and least effective paths is really quite fun. The example below is leveraging Analytics data collected from a Launch Sitecore instance:
Selecting a path by clicking on the map, leading from a mail campaign to a login page yields the results below. Clicking on an element within the funnel of a selected path visit shows the exit path from that particular page:
You can also select a path from the lists in the right navigation, narrowing down on a particular path of high importance. For instance, the selected path below is one of the most efficient full paths:
Showing the funnels of the select path:
Social is delivered out of the box with Sitecore 8. Previously, this required a separate installation of the Socially Connected module from SDN.
Available from the Page Editor, the “Messages” button under “Social” allowing content editors to create, edit and post a message on a target network. Take note of the new “Social” node in the content tree directly under the Sitecore root node.
While new pipeline processors have been added to accommodate the new Sitecore 8 features, others have been moved with Content Testing and Experience Editing in mind. Below are the main areas of change with regards to pipeline processing:
- Social related pipelines come configured out of the box.
- FXM related hooks
- Everything Analytics
- Moving of existing pipelines, such as:
- SPEAK components
- Experience Editor
- Web API request handling (higher up in the httpRequestBegin pipeline)
- RenderField (immediately after httpRequestBegin)
Outside of Analytics, Sitecore.ContentTesting.config changes dominate the difference in Sitecore 8. Take a look at the config patch file. Content tests need to insert themselves in areas to override existing Sitecore rendering and processing handlers, such as:
- Insert Renderings
- Data Aggregation
- Database Agent (background processing to determine if a test has reached statistical relevancy)
- Content Testing provider (used by ItemManager)
The ItemManager’s default item provider is now the content testing item provider, which is essentially a wrapper of the existing Sitecore.Data.Managers.ItemProvider overriding GetItem(). It is here where other versions/variations of items used via Content Testing are obtained for rendering.
For more information regarding ItemManager and providers, check out this post on overall Sitecore data architecture.
One final interesting remark regarding configuration. You can now override the standard server time zone if needed via the ServerTimeZone setting.
Sitecore 8 offers a much better editing experience. It’s faster, cleaner and easier to work with. It’ll be interesting to see the various modifications to content tests and how, as developers, we can leverage this data to build modules and further enhance the client.
When running a Sitecore Azure deployment, ever see this error?
“The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters”
This is not an error specific to Sitecore.
In an attempt to better understand the Sitecore Azure API and find the files/paths preventing a successful deploy, I built the Sitecore Azure Build Verifier. This tool will execute a dry-run of a Sitecore Azure deploy, alerting you of files and folders which fail validation.
Follow the instructions within the project ReadMe to install.
Configuring a Build
What I found interesting about this problem was the fact that I had installed a test version of Sitecore 7.2 with Azure 7.2 (rev 140411) under the path C:\inetpub\wwwroot_sandbox\Azure72 — not an overly verbose instance name or root path location.
For those of you new to Azure in Sitecore, specifying the target location for builds is located within an Environment item, under the field Build Folder.
As you can see, the default is C:\inetpub\wwwroot_sandbox\Azure72\Data\AzurePackages. Sitecore Azure will, also by default, append additional folders to this path for each build instance executed from within the Azure module interface resulting in a build path such as:
Quickly looking at the path above, you’ll notice references to:
- Build instance
- Environment name
- Role instance and name
- DNS host name
Simply change the build folder to something with a shorter path. For example, set your path to C:\AzureBuild and you’re past this issue.
Verifying The Build
If you’re interested in which files/folders are preventing a successful deploy, install the Azure Build Verifier module. After installation, select an Azure Deployment item and right-click. Select the “Verify Deployment” option:
Upon making the selection, you’ll be presented with a dialog box displaying the list of files and paths which exceed the limits previously seen in the failed deployment:
If you’re interested in the implementation, check out the GitHub repo. Dig into the artifacts processor or the main verify class leveraging the Sitecore.Azure library to resolve its sources prior to processing.
Details within this post outline how to obtain basic settings, global variables and Azure module items from Sitecore’s Azure API. Included are also some pitfalls to be aware of when working with the library.
The Settings object allows for access to the different settings defined within Sitecore.Azure.config.
EnvironmentsPath, for example, is exposed through the following getter:
GlobalVariables collection allows for retrieval of any
sc.variable defined within the site’s web.config.
Obtaining a global setting:
var dataFolder = Settings.GlobalVariables["dataFolder"];
Configuration settings is not the only data exposed through the Settings class, you can also obtain:
- Environment Definitions – A collection of all environments defined under the Azure module root item.
- Environments Root – The Azure module root item.
- Vendors – Collection of all vendors under the Azure module root item.
Obtaining Azure Item Wrappers
Looking up specific Azure items using the standard Sitecore.Data.Items.Item object is fine, however, the Sitecore Azure API offers an ORM of sorts to access this information. You can instantiate these objects by passing in their related Sitecore item.
The example below shows how you can obtain an Azure deployment item and walk up the tree by accessing each item’s parent until we reach the environment item root.
Obtaining Specific Azure Items
To obtain Azure specific objects, follow the patterns below to access. Take note of the pitfalls section when considering this approach.
There are multiple ways to obtain an environment from the API. You can get all environment definitions:
var environments = Settings.EnvironmentDefinitions;
Explicitly target an enviornment type. Using a local emulator as an out-of-the-box example:
Local Emulator is defined in the Azure Environment configuration file under App_Data\AzureEnvironments\
Once an environment is obtained, getting an instance of a location is rather simple. You can Explicitly get a location:
var location = environment.GetLocation("localhost");
Iterate over a collection of locations:
var locations = environment.Locations;
Similar to the approaches above, a single farm can be obtained by deployment type:
var farm = location.GetFarm("Delivery01", DeploymentType.ContentDelivery);
as a collection:
var farms = location.Farms;
Obtain a single instance by name:
var role = farm.GetWebRole("Role01");
or assuming Role01 exists for a common Azure configuration:
var role = farm.WebRole01;
as a collection:
var roles = farm.WebRoles;
Obtain a single instance by deployment type:
var deployment = role.GetDeployment(DeploymentSlot.Production);
as a collection:
var deployments = role.Deployments;
The deployment object exposes deployment settings from the API as well. In the example below, we leverage the
FilePathFilter object to access deployment item settings:
Exposing these fields:
Item Creation on Object Instantiation
While the API itself is easy to use, it’s essential to understand what’s happening behind the scenes. Whenever instantiating an object deriving from the abstract class
AzureEntity, if the requested item does not exist, the framework will automatically create the item for you. I’ve seen this happen specifically for location and farm items. Make sure your name matches the entry you’re looking for, otherwise, you’ll have extra items created under the Azure module branch in the content tree.
Consider a scenario of requesting a location of MyLocation. The Azure location item does not exist. I’ll run the following code:
Suppose I then request the farm MyFarm which also does not exist in content:
This results in those items being created by the API:
Web Forms for Marketers provides flexibility on part of content editors to create and manipulate simple forms for collecting user data. While there’s a great deal of flexibility on the back-end, customizing the format and layout of the form can sometimes be a more difficult task.
Knowing what some of the limitations are regarding the rendered markup of Web Forms for Marketers, I wondered how difficult it would be to override the rendering of the form to allow drastic changes to the standard look-and-feel of the out-of-the-box implementation. Sure, content edits can add CSS classes to content, but I wanted to push the envelope and see how far we could go.
Now, I’ll be the first to admit when you’re looking to use a feature like Web Forms for Marketers, it’s absolutely necessary to know the strengths and limitations prior to recommending a specific implementation plan. Working closely with clients and designers is critical to a successful implementation. One that can be managed well over time by content editors, but remains on the upgrade path for future updates by Sitecore.
Introducing Razor Views for Marketers
The goal of this project is simple. Override the rendering of Web Forms for Marketers forms to take full control of the rendered markup.
Purely out of research, Razor Views for Marketers was built to extend the rendering of the form. Content remains the same, as well as the structure and templates within Sitecore. Razor Views for Marketers simply replaces how forms are rendered and how validation is conducted against view models. Page editor also works.
Razor Views for Marketers uses Blade to take advantage of MVC-style razor view templating, allowing us to leverage MVC editor templates and dynamic model binding. The razor views give complete control over how fields are rendered, as well as razor views for field sections and the form itself.
As a proof of concept, I set out to replace the “Leave a Message” form. Out-of-the-box implementation renders the form as:
With a Single-Line Text field rendering as:
The Razor Views for Marketers Implementation, using the following set of Razor views:
Sections, implemented with editor templates:
and fields also have editor templates. Example of a Single-Line Text:
Through helper methods, we’re able to maintain Page Editor functionality while rendering Web Forms for Marketers fields. Hidden fields are necessary for dyanmic model binding on postback for the form.
Updated rendered output of the Single-Line Text field from the razor view above:
The Razor Views for Marketers core logic wraps the existing SimpleForm to perform operations such as:
- Submitting the form
- Collecting WFFM save actions
- Collecting form field control results
- Expose the form’s FormItem
The wrapper is essentially an empty shell of the form, allowing us to leverage the existing WFFM framework for submitting forms through the standard Sitecore WFFM pipelines.
A field decorator encapsulates existing WFFM field control results for form processing.
If you’re interested in this approach, check out the repo.
Recommendations on how to contribute are included. The framework supports model binding of all existing Web Forms for Marketers fields, however, views and validators have been created to accommodate only those fields within “Leave a Message” form.
When within the context of a WFFM field custom validator, determining whether or not a field is required may not be a straight-forward task. Understand that within the context of the validator, there are two ways of obtaining an instance of a form:
1. Obtain an instance of the current SimpleForm control, find your field and check if any MarkerLabels are present
2. Obtain an instance of the WFFM form itself, find your field item and check if its Required field is checked
Approach 1: Obtain an Instance of the current SimpleForm
Here we’re getting an instance of the SimpleForm using WebUtil.GetParent<SimpleForm>(). From there, we’re searching for our field, also via WebUtil. One very important thing to keep in mind is that the instance of the form we’re getting is the rendered output of the form itself and not the actual form item in Sitecore content. This makes it quite difficult to determine the checked state of the required checkbox on our form field. Instead, we’re dependent upon doing an .Any() on the “Requred” property of our field to see if any RequiredWithMarkerValidator items exist.
Shout out to Mike Reynolds for the idea of running .Any() on the Requred property.
Approach 2: Obtain an Instance of the current WFFM Item
Here we’re still getting an instance of the SimpleForm using WebUtilGetParent<SimpleForm>(), but now we’re using the FormId property on that control to get the Sitecore ID of the form. We can then use this to get the item in Sitecore content to read the “Required” field value and determine its checked state.
Obviously, you’ll want to do additional null checking and most likely use an ORM like Synthesis to get strongly type objects mapping back to our WFFM forms and field items.