In one of our projects we have News page type. It has a “see more” section with links to other news pages. When editor is creating New page, he adds few articles to “see more” section. In some situations he would like to pick few random pages. I extended ContentArea with adding random items functionality.
The random list will be generated on server side and returned by REST controller. It’s a simple implementation which use FindPagesWithCriteria to get list of available pages. After loading collection, three random items are returned.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
using System; using System.Linq; using System.Web.Mvc; using ContentAreaWithPreview.Models.Pages; using EPiServer; using EPiServer.Core; using EPiServer.DataAbstraction; using EPiServer.Filters; using EPiServer.Shell.Services.Rest; namespace ContentAreaWithPreview.Business { [RestStore("randomContentStore")] public class RandomContentStore : RestControllerBase { private IContentTypeRepository _contentTypeRepository; private IPageCriteriaQueryService _pageCriteriaQueryService; public RandomContentStore(IContentTypeRepository contentTypeRepository, IPageCriteriaQueryService pageCriteriaQueryService) { _contentTypeRepository = contentTypeRepository; _pageCriteriaQueryService = pageCriteriaQueryService; } public ActionResult GetRandomContentReferences(RandomContentParams randomContentParams) { var criterias = new PropertyCriteriaCollection { new PropertyCriteria { Condition = CompareCondition.Equal, Name = "PageTypeID", Type = PropertyDataType.PageType, Value = this._contentTypeRepository.Load(nameof(NewsPage)).ID.ToString(), Required = true }, new PropertyCriteria { Condition = CompareCondition.NotEqual, Name = "PageLink", Type = PropertyDataType.PageReference, Value = randomContentParams.ContentId.ID.ToString(), Required = true } }; var newsPageItems = this._pageCriteriaQueryService.FindPagesWithCriteria(ContentReference.StartPage, criterias); const int numberOfItems = 3; if (newsPageItems.Count > numberOfItems) { var rng = new Random((int) DateTime.Now.Ticks); var values = Enumerable.Range(0, newsPageItems.Count).OrderBy(x => rng.Next()).ToList(); return this.Rest(values.Take(numberOfItems).Select(v => newsPageItems[v].ContentLink.ID)); } return this.Rest(newsPageItems.Select(p => p.ContentLink.ID).ToArray()); } } public class RandomContentParams { public ContentReference ContentId { get; set; } } } |
In module initializer, the JsonRest store is registered under the “randomContentStore” key.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
define([ "dojo", "dojo/_base/declare", "epi/_Module", "epi/dependency", "epi/routes", "epi/shell/store/JsonRest" ], function ( dojo, declare, _Module, dependency, routes, JsonRest ) { return declare([_Module], { initialize: function () { this.inherited(arguments); var registry = this.resolveDependency("epi.storeregistry"); //Register store registry.add("alloy.randomContentStore", new JsonRest({ target: this._getRestPath("randomContentStore"), idProperty: "contentLink" }) ); }, _getRestPath: function (name) { return routes.getRestPath({ moduleArea: "App", storeName: name }); } }); }); |
The module initializer is set in clientModule section of module.config and it will be executed during loading framework.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="utf-8" ?> <module> <clientResources> <add dependency="epi-cms.widgets.base" path="scripts/moduleInitializer.js" resourceType="Script" /> </clientResources> <clientModule initializer="contentAreaWithRandomItems.moduleInitializer"> <moduleDependencies> <add dependency="Shell" type="RunAfter" /> </moduleDependencies> </clientModule> <dojo> <paths> <add name="contentAreaWithRandomItems" path="scripts" /> </paths> </dojo> </module> |
The widget inherits from ContentArea editor and adds additional “Add random items” link. It has to be added after standard “create a new block” action, so createAddRandomElementsAction method is executed when getTemplateString is resolved. In link click event the REST service is called and random elements are added to the ContentArea.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
define([ "dojo/_base/declare", "dojo/_base/lang", "dojo/when", "dojo/on", "dojo/dom-construct", "epi/dependency", "epi-cms/contentediting/editors/ContentAreaEditor" ], function ( declare, lang, when, on, domConstruct, dependency, _ContentAreaEditor ) { return declare([_ContentAreaEditor], { buildRendering: function () { this.inherited(arguments); var registry = dependency.resolve("epi.storeregistry"); this.randomContentStore = registry.get("alloy.randomContentStore"); var store = registry.get("epi.cms.content.light"); function addRandomLinks(items) { for (var i = 0; i < items.length; i++) { dojo.when(store.get(items[i]), lang.hitch(this, function (contentData) { this.model.modify(lang.hitch(this, function () { this.model.addChild(contentData); })); })); } } function createAddRandomElementsAction() { var linkNode = domConstruct.create("a", { "class": "epi-visibleLink", "innerHTML": "Add random items" }); var linkContainer = domConstruct.toDom("<div></div>"); domConstruct.place(linkNode, linkContainer); domConstruct.place(linkContainer, this.actionsContainer, "last"); on(linkNode, "click", lang.hitch(this, function () { if (!this.tree) { this._createTree(); } when(this.getCurrentContent()).then(lang.hitch(this, function (currentContent) { when(this.randomContentStore.executeMethod("GetRandomContentReferences", 'getRandomContentReferences', { contentId: currentContent.contentLink })) .then(lang.hitch(this, addRandomLinks)); })); return false; })); } when(this.getTemplateString(), lang.hitch(this, createAddRandomElementsAction)); } }); }); |
The full source code is available on Gist