Overview
Hybrid approaches such as this have been used in other technology industries with great success. Consider the world of Content Delivery Network (CDN) services. In this environment, frequently accessed static content is loaded in the CDN cache. Whereas, the dynamic content that cannot be cached remains on the origin servers. In the same way, the CrownPeak CMS uses both a decoupled architecture along side a list of dynamic delivery options. The decoupled architecture has several benefits:
- The deployment of content can be controlled through workflow stages (Draft, Stage, Prod) and access control groups (Authors, Approvers, Developers, Administrators)
- The website content is instantly available with no delays (caused by dynamic rendering engines)
- The website framework and technology is chosen by the customer based on their preference (the CrownPeak CMS is technology agnostic)
- The website content and generated URL’s are SEO friendly, and do not require the URL rewriting and caching techniques required by dynamic rendering engines
- The environment is always highly available (HA) with very high uptime percentages (99.9%+)
- The infrastructure can scale to accommodate extra website traffic as needed
- Several dynamic options (See below) can be added to the platform for a complete solution
Content Options in the CMS
Static content is often used in general page layouts including: headers, footers, menus, and reusable content widgets as well as images, JavaScript files, and CSS settings. These do not change often and can benefit from a high speed hosting delivery environment. Dynamic content is a key aspect of most modern websites today. The use of animation, rotating images, document lists, and changing content creates a rich experience for website visitors. Content is key to the development of dynamic websites. There are a number of different forms of dynamic content that can be leveraged in your CMS project. The table below highlights the content types that are available, the method of storage and the best way to access each type of data in the CrownPeak Platform.
# | Content Type | Stored As | Accessed via CrownPeak |
1 | Document files (.doc, .xls, .pdf) | Files on Web Servers | Search G2 |
2 | Related Content (based on taxonomy) | Files on Web Servers | Search G2 |
3 | Content Lists (Products, Locations, etc) | Files on Web Servers | Search G2 |
4 | Lists of products, locations, etc. | Files on Web Servers | Search G2 |
5 | Configurator / Calculators | Rows in SQL DB Tables | Database as a Service |
6 | Membership Management Data | Rows in SQL DB Tables | Database as a Service |
7 | User Generated Data (ex: comments) | Rows in SQL DB Tables | Database as a Service |
8 | Transactional Data | Rows in SQL DB Tables | Database as a Service |
9 | Webpage content and metadata | CMS Repository | CMS Access API |
10 | Content based on personas (Targeting) | WCO Repository | WCO Snippets in HTML |
11 | Content Optimization (A/B Testing) | WCO Repository | WCO Snippets in HTML |
Filtering Your Results
All of these content options can be returned into your website and web application with the ability to directly filter the content as well as apply faceting, sorting, and pagination options (Figure 1). However, the options for filtering dynamic content depend on the type of data and the storage mechanism. The table below illustrates the CrownPeak dynamic options and the methods of filtering your data. In addition, CrownPeak has created examples in several languages (C#, JavaScript, Node.js, Powershell, Knockout.js) to help users take advantage of the CMS Platform.
# | CrownPeak Dynamic Options | Filtering Options | Examples Available In |
1 | Search G2 | Filter Parameters | Knockout.js, C# |
2 | Database as a Service (DaaS) | SQL Where Clauses | C# |
3 | Content Optimization (A/B Testing) | Quota Based Sampling | JavaScript |
4 | Content based on personas (Targeting) | Targeting Criteria | JavaScript |
5 | CMS Access API | Filtering | Node.JS, C#, PowerShell |
Figure 1. Dynamic Content Filter Options
Dynamic Content – Search G2
Volumes of files such as MS Word documents, Excel Spreadsheets, and Acrobat PDF files reside on the storage attached to the web servers in the hosting environment. CrownPeak Search can be used to crawl these files and create an indexed search results collection. This collection can be presented as a traditional faceted search results page or as dynamic content on a web page shown below (Figure 2).
Figure 2. http://mtistage.cp-access.com/searchg2/cheese-shop/en/
Other data files such as HTML, XML, and JSON reside on the storage attached to the web servers in the hosting environment. CrownPeak Search can be used to crawl these files and create an indexed search results collection. This collection can be presented as a traditional faceted search results page or as dynamic content on a web page shown below (Figure 3).
Figure 3. http://stagetraining2.cp-access.com/SearchG2/JavaScript-SDK-Showcase/geo.aspx
Search Request Example The code for the pages in Figure 2 and Figure 3 are KnockoutJS and Javascript accessing the search index collection.
Dynamic Content – DaaS
Large amounts of data are often stored in SQL databases and other storage repositories. CrownPeak Database as a Service (DaaS) can be used to access data stored in SQL database tables. The selected “results set” can be queried and presented as lists of dynamic content on a web page.
DaaS Code Example The code for the page in Figure 3 is a CMS C# .Net output template accessing the lists of data stored in the database.
< < input type="search" id="searchInput" placeholder="What are you looking for?" autofocus="" data-bind="value: query, event: { keyup: searchKeyUp }" > < script type="text/javascript">//
Dynamic Content – CMS External Access
The primary location of content and metadata is the CMS SaaS repository. The CMS Access API is an external library that enables web developers to directly access the CMS repository from outside the CMS. Developers can now create, import, and manage CMS Assets, Configurations, and Workflows through a set of public API calls protected by access controls and rate limiting governors.
Java Script Example This basic example of the accessAPIModule to interact with the CrownPeak CMS AccessAPI. The helper libraries for Node.js are available for download on Connect.
var requestify = require('requestify'); var prompt = require('sync-prompt').prompt; var colors = require('colors'); var fs = require('fs'); var apikey = ""; var domain = ''; var instance = ''; var cookies = null; var username = ''; var password = ''; //read a json file with configuration fields, so we don't have to hard code them if (fs.existsSync('config.json')) { console.log('reading config.json'.yellow.bold); var config = JSON.parse(fs.readFileSync('config.json', { "encoding": "utf8" })); instance = config.instance; domain = config.server; apikey = config.accessKey; username = config.username; console.log('instance: ' + config.instance); console.log('domain: ' + config.server); console.log('username: ' + config.username); } var hidden = true; //prompt user for password if (password.length == 0) password = prompt('Password: ', hidden); var baseURL = domain + '/' + instance + '/cpt_webservice/accessapi'; //setup http headers var options = { body: '', method: 'POST', headers: { 'x-api-key': apikey, 'Content-Type': 'application/json; charset=utf8', //'Accept-Encoding': 'gzip, deflate ' }, }; exports.auth = function (callback) { var body = { "instance": instance, "username": username, "password": password, "remember_me": false, "timeZoneOffsetMinutes": -480 }; return restPost('/auth/authenticate', body, callback); } exports.logout = function (callback) { return restPost('/auth/logout', null, callback); } exports.AssetExists = function (path, callback) { var body = { "assetIdOrPath": path }; return restPost('/asset/Exists', body, callback); } exports.AssetUpload = function (newName, folderId, modelId, workflowId, bytes, callback) { var body = { "newName": newName, "destinationFolderId": folderId, "modelId": modelId, "workflowId": workflowId, "bytes": bytes }; if (folderId == 0 || folderId == undefined) return callback('not allowed to import to root'); return restPost('/asset/Upload', body, callback); } exports.AssetCreate = function (newName, folderId, modelId, type, devTemplateLanguage, templateId, workflowId, callback) { var body = { "newName": newName, "destinationFolderId": folderId, "modelId": modelId, "type": type, "devTemplateLanguage": devTemplateLanguage, "templateId": templateId, "workflowId": workflowId } if (folderId == 0 || folderId == undefined) { console.log('create asset error, folderId = ' + folderId); return callback('not allowed to import to root'); } return restPost('/asset/Create', body, callback); } //main http call function restPost(url, body, callback) { url = baseURL + url; console.log("calling: ".yellow.bold + url.green.bold); options.body = body; //check if we need pass the cookies if (cookies != null) { // todo: try to reuse headers from above. options.headers = { 'x-api-key': apikey, 'Content-Type': 'application/json; charset=utf8', 'Cookie': cookies //'Accept-Encoding': 'gzip, deflate ' } } requestify.request(url, options).then(function (resp) { processCookies(resp); callback(JSON.parse(resp.body)); }, function (err) { //todo: handle http 429, rate limiting busy, retry-after callback(JSON.parse(err.body)); }); } //handles cookies between http calls function processCookies(resp, callback) { if (resp.headers['set-cookie'] != null) { var cooks = resp.headers['set-cookie']; var newCookies = ''; var cookieCount = 0; cooks.forEach(function (cookie) { var parts = cookie.split(';'); //console.log(parts); if (cookieCount++ > 0) { newCookies += "; "; } newCookies += parts[0]; }); cookies = newCookies; } }
Dynamic Content – Testing & Targeting
Another aspect of dynamic content is creating targeted content for different persona groups visiting the website. You can create target group criteria such as:
- Ambient data including: Time, Date, Referrer, Keyword, Browser, and location
- Form data entered on a previous page
- Behavioral on the site such as pages already visited.
- 3rd party integration data such as Marketo, Demandbase, etc
WCO Snippet Example A simple JavaScript tag placed on a webpage is all that is needed. WCO will do the targeting and present the correct content on the page. For example this tag will present:
<script type="text/javascript" src="//snippet.omm.crownpeak.com/s/ b9dcf532-8694-4b5c-91dc-3cf7155ad70a" >
Website visitors that are located on the US East Coast will see the following message:
“Come to New York City this summer. Visit the Statue of Liberty and see a Broadway show.”
While Website visitors that are located on the US West Coast will see the following message:
“Sunny Southern California is the home to Beaches, Disneyland, Sea World and more…”
Another aspect of dynamic content is creating content tests that randomly present different versions of the content to website visits based on a percentage allocated to each content variant. Consider a test with 2 content options here presenting content 50% per of the time.
WCO Snippet Example A simple JavaScript tag placed on a webpage is all that is needed. The WCO testing snippet (with 2 content options) will use a quota base sampling selection process to present 50% of website visitors with the “A” test content and the other 50% of website visitors with the “B” test content. For example this tag will present:
<script type="text/javascript" src="//snippet.omm.crownpeak.com/s/ b9dcf532-8694-4b5c-91dc-3cf7155ad70a" >
50% of website visitors that with see the “A” test content:
“Summer is the time for barbecuing, fun in the sun, and amusement parks.”
50% of website visitors that with see the “B” test content:
“Sunny Southern California is the home to Beaches, Disneyland, Sea World and more…”