<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Hoang Tran</title> <link>https://world.optimizely.com/blogs/hoang-tran/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Connecting a remote EPiServer database to an EPiSever CMS, or enable a CMS to use multiple databases</title>            <link>http://hoang-tranminh.blogspot.com/2015/10/connecting-remote-episerver-database-to.html</link>            <description>Recently I worked on a project with a special need, that is to merge two similar EPiServer CMS 6R2 sites into one CMS. Legacy content are mostly read-only. We could have many solutions to deliver this requirements, some of commons solutions I could think of such as:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Merge the code bases into one and migrate content so that we have one database that contains content of both sites.&lt;/li&gt;&lt;li&gt;Merge the code base, and connect a database to other database using an open source project&amp;nbsp;&lt;a href=&quot;http://remotepageprovider.codeplex.com/&quot; target=&quot;_blank&quot;&gt;remote page provider&lt;/a&gt;&amp;nbsp;, which is a WCF service based solution.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;And there is a third option I could think of, it could sounds crazy that is to connect a database to another database using page provider, but connecting it directly using SQL connection, not through any service layer.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Actually I would create a new, small database for the new CMS, then connecting old databases into that CMS using SQL page provider. We will have a Search engine to index content of all the databases, used to support listing, querying, filtering, sorting, paging data across databases. We should index only data that we intended to search to keep the index size small and fast.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here the points that I could argue about this solution, compared to other solutions&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Advantages:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&amp;nbsp;The new CMS site would start based on a small database, it would be faster to start then other solutions, in which each legacy database has million of pages. Working with new data in new CMS will be &amp;nbsp;also quicker, because it operates on a small database. Legacy content in old databases are read only most the time, and they are only accessed to only if they are specifically referenced to.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;And accessing old content using SQL connection is faster than using any service layer.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;We avoided a long, painful and error prone migration process, specially with a database with million of pages, images, documents referencing each other in a complex way. Migration should always be the last option, and it seems impossible with this size of data.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;We avoided having to keep the legacy CMS sites running, wasting resources in case we use WCF remote page provider.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Old content is accessed in new CMS through page provider concept, which is a well supported by EPiSever CMS so all data operations works well ( as it&#39;s supposed to :) ). The old content will have same ID in new system, but with a provider name tag in its PageReference. New content can reference old content normally, permanent link feature works well, and our search engine will be used extensively for&amp;nbsp;listing, querying, filtering, sorting, paging data across databases. This search engine will enable editors feels that all data is still in one place.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Disadvantages:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;A page provider that can provider EPiSever pages from a remote EPiServer database it not readily available, and it could be impossible if EPiSever architecture doesn&#39;t allow it.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Hanlding files and images from remote database and VPP also could be a challenge.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Since we access data directly in database, if some how old CMS site is turned on and old data is updated, we could have trouble in synchronizing data. Actually we can handle this quite easy by connecting remote event mechanism between old CMS and new CMS sites.Or we can develop a small service in new CMS to clear the cache when old site updates data in database.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;All of these merging solutions will have to handle friendly URL rewriting or redirect for old content.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;OK, so far it sounds good right ? We have to do a proof of concept so that we know it could work. I have successfully created a Page Provider that uses SQL connection to read pages from a remote EPiServer database, and also a VirtualPathProvider that can read files from remote database. I could introduce the direction of the solution in this blog, because I believe it could be useful in some situations such as:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;We merge similar sites into one such as my project requirements&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;We have a huge existing site, with millions of old content that are very difficult to clean up, remove or backup, and it slows :). We could start with a new, small database to work with, and connecting old database into the new CMS to still be able to access old content.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;We can build a master CMS site, that can manage content of many other &quot;child&quot; CMS sites. And the use of this ? I&#39;m not sure yet, but maybe someone will like this :)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Or we could build a system in which importance, fast growing model reside in separate databases for performance reason. And all of these databases are standard EPiServer ones we we won&#39;t have to worry about upgradability.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;Here are some importance details of my solution:&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;EPiServer internally use a page provider called LocalPageProvider, which uses many of data access classes to build up pages from local database tables. So my thought is, is it possible to re-use those data access classes, and just injecting a remote database connection string to them, so that they can fetch pages from a remote database ?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I can see that this question is not officially supported by EPiServer, architecture-wise, or at least from what I know, but I found a way to hack this :)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All data access classes written by EPiServer use the same base class, and this base class, DataAccessBase use a static object called DatabaseFactory to provide connection string setting for most of the database access calls. I did a simple work around, I wrote a composite DatabaseFactory object, returning local or remote connection string based on a global context variable, injected this into DatabaseAccess class. After that I created a page provider, inheriting from LocalPageProvider, like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-0M87CEwMOpI/VjCKN2oO9YI/AAAAAAAAIWA/cb5UM6ZlPYc/s1600/a1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;128&quot; src=&quot;http://3.bp.blogspot.com/-0M87CEwMOpI/VjCKN2oO9YI/AAAAAAAAIWA/cb5UM6ZlPYc/s640/a1.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can see that we set a context data, this is locally per thread or per request, then call the base class to do the database access work. And our CompositeSqlDatabaseFactory will switch connection string based on this context data. But ofcourse we have to &quot;massage&quot; values from remote database to appropriate values before returning the page, this includes page type ID, page type name, page reference (adding the provider tag name), page LinkURL. This works well in a multi thread scenario, it won&#39;t affect how EPiServer accessing local database, and we have reused a lot of data access code written by EPiServer :)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There is one catch here though, most links in EPiServer database are stored in permanent link format, which basically a Guid. EPiServer CMS frequently have the need to resolve a Guid into a PageReference, and usually it has to scan all PageProvider for the resolving work, and this is could be the place for performance bottleneck, when we connect a large database into a small database, the site will become slower because of this scanning. But, we can cache all the Guid that are not existed in our SQL page provider and this could preserve my asumptions about the performance improvement with this solution.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;OK, next challenge is about the VPP, when I tried to dig deep into the code to find out if it is possible or not, and I was really happy to find out that VirtualPathVersioningProvider uses IObjectStore concept, and we could inject a remote database connection string into an IObjectStore, then inject it into a virtual path provider inheriting from VirtualPathVersioningProvider. But, I was disapppointed in the end, all the classes that are used for file versioning system use the injected IObjectStore correctly, except the VirtualPathVersioningProvider class, it always use the default, local database IObjectStore :| &amp;nbsp;! I think this is a bug in the implementation. I have to overcome this with some reflection code, but in the end, it works beautifully.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I hope can make this concepts work on CMS version 8+, and maybe have time to explore a scenario in which we have a multisite CMS, with each site in on a separate database also.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Thanks for reading. I&#39;m working for a client so I can&#39;t share my code, I think you understand that :).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>            <guid>http://hoang-tranminh.blogspot.com/2015/10/connecting-remote-episerver-database-to.html</guid>            <pubDate>Wed, 28 Oct 2015 10:41:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Applying ajaxify plugin to an existing website, wrong script exec order fixed, example on EPiServer 8 Alloy.</title>            <link>http://hoang-tranminh.blogspot.com/2015/10/applying-ajaxify-plugin-to-existing.html</link>            <description>For anyone that have not heard about Ajaxify plugin before, please &lt;a href=&quot;https://github.com/browserstate/ajaxify&quot;&gt;go here&lt;/a&gt;, it&#39;s a small plugin that transform links on your website into ajax request, keeping part of your site static, and replace new content from response of ajax request into other part.&lt;br /&gt;&lt;br /&gt;I presented a most simple scenario in using this plugin in an image below:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-6DH2eN_4or0/Vg34M3Jpk9I/AAAAAAAAIU8/UhRs8oJovAE/s1600/img1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;300&quot; src=&quot;http://3.bp.blogspot.com/-6DH2eN_4or0/Vg34M3Jpk9I/AAAAAAAAIU8/UhRs8oJovAE/s400/img1.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;In above scenario, links within the content area will be transformed by ajaxify plugin, clicking on a link will initiate an ajax request to server, and fetching full new HTML page from server.&lt;br /&gt;&lt;br /&gt;This plugin handle URL changes and browser history correctly using History.js, and it also execute all the scripts inside the content area only, not script in header or footer, in ajax response again.&lt;br /&gt;&lt;br /&gt;If we have a site with very clean coding style, all the scripts should be at the bottom of the site, no script links or script blocks should be inside the content area, it would be perfect. But the topic I presented here is about how to apply this plugin on an existing, probably legacy site, in which you might have plenty of script links or script blocks within the content area.&lt;br /&gt;&lt;br /&gt;I discovered an issue with ajaxify plugin when applied it on an existing site, when executing the scripts within content area from ajax response, the scripts order is messed up, script blocks is always executed first, in correct order, but script links will be executed after the script blocks, event the script links appear before script blocks. By script execution order I mean the order they appeared in HTML from top to bottom. All browsers when loading an HTML page normally (not by ajax) will always execute scripts in order they appear in HTML, from top to bottom, and they may download the script links in parallel or get from cache .&lt;br /&gt;&lt;br /&gt;For example I have following scripts inside my content area:&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;/Static/js/BigExternalJavascripts.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script src=&quot;/Static/js/SmallExternalJavascripts.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;&lt;br /&gt;            (function () {&lt;br /&gt;                var localVariable = &quot;script block 1 executed&quot;;&lt;br /&gt;                console.log(localVariable);&lt;br /&gt;            })();&lt;br /&gt;            &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;!-- some HTML --&amp;gt;&lt;br /&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;&lt;br /&gt;            (function () {&lt;br /&gt;                var localVariable = &quot;script block 2 executed&quot;;&lt;br /&gt;                console.log(localVariable);&lt;br /&gt;            })();&lt;br /&gt;            &amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&quot;BigExternalJavascripts.js&quot; is 870KB, &quot;SmallExternalJavascripts.js&quot; is just 1 KB, both of them just&lt;br /&gt;do very simple task, log a message &quot;X external script is executed&quot;, X is big or small&lt;br /&gt;&lt;br /&gt;When the page is loaded normally, our debug console will show this:&lt;br /&gt;&lt;br /&gt;&quot;Big external script executed&quot;&lt;br /&gt;&quot;Small external script executed&quot;&lt;br /&gt;&quot;script block 1 executed&quot;&lt;br /&gt;&quot;script block 2 executed&quot;&lt;br /&gt;&lt;br /&gt;When new content is applied from ajax response, our debug console will show this:&lt;br /&gt;&lt;br /&gt;&quot;script block 1 executed&quot;&lt;br /&gt;&quot;script block 2 executed&quot;&lt;br /&gt;&quot;Small external script executed&quot;&lt;br /&gt;&quot;Big external script executed&quot;&lt;br /&gt;&lt;br /&gt;While this maybe not a trouble for many sites, it could be trouble to some. I developed a function to fix this script loading order, assume that $scripts are all the scripts element inside content area:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;//function to load scripts in correct execution order&lt;br /&gt;  var scriptLoader = function ($scripts) {&lt;br /&gt;&lt;br /&gt;      //==============Add the scripts back to the document, keeping execution order =============&lt;br /&gt;      // scriptTree is a list of script elements (script blocks or external scripts).&lt;br /&gt;      // For script blocks they are always executed according to order they are appended to the document.&lt;br /&gt;      // For external scripts, they are executed according to order they are appended if they have async=false.&lt;br /&gt;      // If we want some script blocks to execute after an external script, we add those blocks to a child array of&lt;br /&gt;      // an external script, and wait after the external script &quot;onload&quot;, then appending the blocks.&lt;br /&gt;      var scriptTree = [],&lt;br /&gt;                preScriptIsExternal;&lt;br /&gt;&lt;br /&gt;      $scripts.each(function () {&lt;br /&gt;          var $script = $(this), scriptText = $script.text(), scriptNode = document.createElement(&#39;script&#39;);&lt;br /&gt;&lt;br /&gt;          if ($script.attr(&#39;src&#39;)) {&lt;br /&gt;              //external script, add src attr to new script element defined above&lt;br /&gt;              scriptNode.src = $script.attr(&#39;src&#39;);&lt;br /&gt;&lt;br /&gt;              //if external script doesn&#39;t have async attribute, has to set this attr specifically to false&lt;br /&gt;              //to preserve the execution order, same with order we append the external scripts to&lt;br /&gt;              //document object later&lt;br /&gt;              if (!$script.attr(&#39;async&#39;)) {&lt;br /&gt;                  scriptNode.async = false;&lt;br /&gt;              }&lt;br /&gt;              else {&lt;br /&gt;                  scriptNode.async = true;&lt;br /&gt;              }&lt;br /&gt;          } else {&lt;br /&gt;              //a script block, add the content to the new script element&lt;br /&gt;              scriptNode.appendChild(document.createTextNode(scriptText));&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          if (!preScriptIsExternal || scriptNode.src) {&lt;br /&gt;              //If the previous script is NOT an external script then add current script (external or block) directly to the tree.&lt;br /&gt;              //If the previous script is an external script, current script must be an external script to add it directly to the tree.&lt;br /&gt;              scriptTree.push({ node: scriptNode, hasSrc: !!scriptNode.src });&lt;br /&gt;          } else {&lt;br /&gt;              //If the previous script is external and current script is block, then add the block to child array of previous script&lt;br /&gt;              scriptTree[scriptTree.length - 1].inlines = scriptTree[scriptTree.length - 1].inlines || [];&lt;br /&gt;              scriptTree[scriptTree.length - 1].inlines.push(scriptNode);&lt;br /&gt;          }&lt;br /&gt;          if (scriptNode.src) {&lt;br /&gt;              preScriptIsExternal = true;&lt;br /&gt;          }&lt;br /&gt;      });&lt;br /&gt;&lt;br /&gt;      $(scriptTree).each(function (index, value) {&lt;br /&gt;          if (!value.hasSrc) {&lt;br /&gt;              contentNode.appendChild(value.node);&lt;br /&gt;          } else {&lt;br /&gt;              if (value.inlines) {&lt;br /&gt;                  var childScripts = value.inlines;&lt;br /&gt;                  value.node.onload = function () {&lt;br /&gt;                      $(childScripts).each(function (i, v) {&lt;br /&gt;                          contentNode.appendChild(v);&lt;br /&gt;                      });&lt;br /&gt;                  }&lt;br /&gt;              };&lt;br /&gt;              contentNode.appendChild(value.node);&lt;br /&gt;          }&lt;br /&gt;      });&lt;br /&gt;  };&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;OK, let&#39;s go and use our new ajaxify code in a brand new EPiServer CMS 8 Alloy website. By default, ajaxify use this selector to know where is the content area &quot;&#39;&lt;i&gt;#content,article:first,.article:first,.post:first&lt;/i&gt;&quot;, so let&#39;s wrap the body part of Alloy site inside a &amp;lt;div id=&quot;content&quot;&amp;gt;, with my example scripts :)&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-aHgH-AvcZzk/Vg4ul9GV_iI/AAAAAAAAIVM/FP3eQJIDSU0/s1600/img2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;451&quot; src=&quot;http://1.bp.blogspot.com/-aHgH-AvcZzk/Vg4ul9GV_iI/AAAAAAAAIVM/FP3eQJIDSU0/s640/img2.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And add all the needed scripts to our bundle&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-_Lt9934ZgRk/Vg4u8b8OWmI/AAAAAAAAIVU/OIoRFXvRKCw/s1600/img3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;163&quot; src=&quot;http://1.bp.blogspot.com/-_Lt9934ZgRk/Vg4u8b8OWmI/AAAAAAAAIVU/OIoRFXvRKCw/s640/img3.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Then we are all done ? Almost, a first bug :), we have to modify ajaxify to trim new line chars from returned HTML&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-Rq8lRA-4fGs/Vg4v6DBoeYI/AAAAAAAAIVg/aXFZ_clg6vc/s1600/img4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;134&quot; src=&quot;http://1.bp.blogspot.com/-Rq8lRA-4fGs/Vg4v6DBoeYI/AAAAAAAAIVg/aXFZ_clg6vc/s640/img4.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Then it worked as expected, the header and footer part of the site stay static ! Well, but we see that the menu in header has changed when new content come, how can it be ? Well, ajaxify is kind enough to find out our menu on the site, set the link whose href equal to new URL to be active. Here is the selector for the menu&amp;nbsp;&#39;&lt;i&gt;#menu,#nav,nav:first,.nav:first&lt;/i&gt;&#39; , and Alloy menu satisfied this selector, yeah!&lt;br /&gt;&lt;br /&gt;If you browse to a video page on Alloy site &quot;Alloy Track&quot;, we see that the page doesn&#39;t work, because there is a new script in header part supposed to be loaded then the new video page load, and ajaxify only execute scripts inside content area only !&lt;br /&gt;&lt;br /&gt;I have improved this, if you add this comment &amp;lt;!--ajaxify-script--&amp;gt; infront of any &amp;lt;script&amp;gt; element on your site, then those scripts, together with scripts inside content area, will be executed, in correct order !&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-ZrIs34SpPcs/Vg45X4ButMI/AAAAAAAAIVw/aN-Dk8qwYzs/s1600/img5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;57&quot; src=&quot;http://1.bp.blogspot.com/-ZrIs34SpPcs/Vg45X4ButMI/AAAAAAAAIVw/aN-Dk8qwYzs/s400/img5.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now the video page works like charm. This also enable many other scenarios to work, for example, there is a script in header needed to load for every page view, to load some random content to show (well, it&#39;s the Ads :-) ) . Applying this comment &amp;lt;!--ajaxify-script--&amp;gt; in front of that script will make it works beautifully. In fact, applying this comment to any script element, everywhere on your HTML will make that script element to be executed again when new content is loaded. This also means that you can apply ajaxify plugin into your website with &lt;b&gt;minimal modification&lt;/b&gt; of existing scripts on your site.&lt;br /&gt;&lt;br /&gt;Here is the link to my example code:&amp;nbsp;https://github.com/hoang-tranminh/AjaxifyWeb/ . Please let me know if you find any bugs, or you could use my modified ajaxify plugin in your projecs :) Thanks.&lt;br /&gt;&lt;br /&gt;</description>            <guid>http://hoang-tranminh.blogspot.com/2015/10/applying-ajaxify-plugin-to-existing.html</guid>            <pubDate>Fri, 02 Oct 2015 09:59:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>User get logged out after a new web application version is deployed to Azure Cloud</title>            <link>http://hoang-tranminh.blogspot.com/2015/09/user-get-logged-out-after-new-web.html</link>            <description>Recently I came across an Azure based web application project, and the team is having a strange bug that their end users always get logged out after every time they deploy a new version of the system on Azure cloud.&lt;br /&gt;&lt;br /&gt;The application works perfect in the could with many front end servers, which are loaded balanced, and the team thought that they have followed guidelines how to build an web application to be hosted on Azure like not using session, applied distributed cache, used message service bus.&lt;br /&gt;&lt;br /&gt;After some investigation we found out that we have not added a machine key to our web.config file for our application! &amp;nbsp;This make all session cookie and authentication cookie of end users became invalid when we deployed a new version of the application, because these cookies are encrypted with the machine key and every time we deploy, we got new virtual machines and they have new machine keys.&lt;br /&gt;&lt;br /&gt;I used to work in system that the load balancer was configured in non-sticky, round-robin (or preditive) mode, with real hardware servers. With platform like this, without adding a machine key to our config file, or making all the servers have same machine key in another way, the application will failed in the first deploy and we would recognize this mistake sooner :)&lt;br /&gt;&lt;br /&gt;Azure cloud load balancer use sticky mode so the application works smoothly for end users during their sessions, and if Azure also create virtual machines inside a distribution set with same machine key automatically then it would be nice :)</description>            <guid>http://hoang-tranminh.blogspot.com/2015/09/user-get-logged-out-after-new-web.html</guid>            <pubDate>Thu, 01 Oct 2015 08:52:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>The compiler failed with error code -1073741502 on an EPiServer web site</title>            <link>http://hoang-tranminh.blogspot.com/2015/09/the-compiler-failed-with-error-code.html</link>            <description>We have a client website in EPiSever CMS 6 R2 that have run smoothly for a long time, and after a recent release, sometime we got a call from editor saying that some tools, specially admin tools suddenly throw this strange error: &lt;i&gt;The compiler failed with error code -1073741502&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-zrcEuHFEyEg/VfBXorrgV7I/AAAAAAAAIUo/-A92IGR_BzI/s1600/img%2B3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;228&quot; src=&quot;http://3.bp.blogspot.com/-zrcEuHFEyEg/VfBXorrgV7I/AAAAAAAAIUo/-A92IGR_BzI/s640/img%2B3.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;And some other tools or CMS function still run well. We got a workaround by restarting the application pool, this issue seem gone temporary. And from this error it really hard to know where to debug.&lt;br /&gt;&lt;br /&gt;One strange thing is that this error happened random with different tools, even the tools that we don&#39;t make any code changes to it for months. And this problem usually appear in the morning, after a night of non-usage.&lt;br /&gt;&lt;br /&gt;We have google a lot and found many comments, we tried to re-install dotNet framework 4.5.1 on servers, re-registered ASP.Net with IIS, deployed many old code versions to test, clean AS.NET temporary folder, changed app pool identity to Local Service etc. but this problem still occurs.&lt;br /&gt;&lt;br /&gt;There one related link mentioned about the app pool user/identity use over a maximum allowed heap memory, we started to think this is a memory leak problem, because it seems to happen over some hours.&lt;br /&gt;&lt;br /&gt;And looking careafully at our error log, we found an error that happened quite many time, there is a schedule job that automatically generate thumbnails for image, and it failed (throw an exception) with an image uploaded recently with special character in its name ! Further debug we found out that in this job the image file stream was not disposed properly when there&#39;s an exception.&lt;br /&gt;&lt;br /&gt;Fix this thumbnail schedule job really fixed our problem ! Yay ! Hope this will help someone, someday :)</description>            <guid>http://hoang-tranminh.blogspot.com/2015/09/the-compiler-failed-with-error-code.html</guid>            <pubDate>Wed, 09 Sep 2015 18:24:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Linked dropdownlists custom property in EPiServer CMS 8 using Dojo widget, with EPiServer reststore, client module initilizer</title>            <link>http://hoang-tranminh.blogspot.com/2015/09/linked-dropdownlists-custom-property-in.html</link>            <description>&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;&lt;strong&gt;Target readers&lt;/strong&gt;: EPiServer developer&amp;nbsp;that is new to EPiServer CMS 8, or new to developing custom property in EPiServer CMS with Dojo widgets. Or if you would like to know how to use a EPiServer reststore inside your widget, or to know a little bit further what is happening around your Dojo widget inside EPierver CMS edit mode, for example how EPiServer pass setting values to your custom property Dojo widget from server side, how EPiServer interact with your widget when saving/loading data.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;&lt;b&gt;Problem definition:&lt;/b&gt;&amp;nbsp;I would like to create a custom property which will show two dropdownlists in EPiServer edit mode, selected value from first dropdown will change the values available for second dropdown without reloading current editing page. And selected values from first and second dropdown will be saved to database. This is useful when you have a list of countries to select first, then the second list will show its cities.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;In my solution, the first dropdown will list all available categories in an Alloy EPiServer CMS 8 site, e.g &quot;cat1&quot;, &quot;cat1/sub11&quot;,&quot;cat2&quot;,&quot;cat2/sub21&quot; etc. After an editor has selected a category from first list, the second dropdown will list all pages that belong to selected category, except the current editing page.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;Technically on a high level, the solution include&amp;nbsp;an EPiServer custom property&amp;nbsp;definition on server side, a Dojo widget with some special API to work nicely with EPiSever edit mode, two EPiServer reststore to provide values for category and page dropdowns. All stuff are packed in a module project.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;&lt;strong&gt;The code:&amp;nbsp;&lt;/strong&gt;now the real stuff. Let&#39;s define a library project named MultipleDropdownProperty, and call our project &quot;multipledropdownproperty&quot; module. We add reference to EPiServer.CMS.Core Nudget package to this project as below:&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-P1SvHi8wrL0/Ve_ab2OJgUI/AAAAAAAAIUM/7cd5cqLc5FQ/s1600/img2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;188&quot; src=&quot;http://2.bp.blogspot.com/-P1SvHi8wrL0/Ve_ab2OJgUI/AAAAAAAAIUM/7cd5cqLc5FQ/s640/img2.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Next we should have an object to represent selected options from first and second dropdown list, the&amp;nbsp;&lt;em&gt;MultipleDropdownChoices&amp;nbsp;&lt;/em&gt;and&lt;em&gt;&amp;nbsp;ChoiceModel&lt;/em&gt;&amp;nbsp;class&lt;/div&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Object to represent selected options of two dropdowns&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public class MultipleDropdownChoices&lt;br /&gt;    {&lt;br /&gt;        public ChoiceModel Choice1 { get; set; }&lt;br /&gt;&lt;br /&gt;        public ChoiceModel Choice2 { get; set; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Represent an option in a dropdown&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public class ChoiceModel&lt;br /&gt;    {&lt;br /&gt;        public string Name { get; set; }&lt;br /&gt;        public string Id { get; set; }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;After this we should define a custom property on server side, that expose&amp;nbsp;&lt;i&gt;MultipleDropdownChoices&lt;/i&gt; as its data type. We inherited from LongString property type and only do some minimal customization so that it expose&amp;nbsp;&lt;i&gt;MultipleDropdownChoices&amp;nbsp;&lt;/i&gt;as data type to application code but inside it serialize&amp;nbsp;&lt;i&gt;MultipleDropdownChoices&amp;nbsp;&lt;/i&gt;object to string to save to database. You can go here to know more about&amp;nbsp;&lt;a href=&quot;/link/733268f8e01e4ad6a398dc66dde46a78.aspx&quot;&gt;EPiServer custom property&lt;/a&gt;&lt;/div&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; &lt;br /&gt;[PropertyDefinitionTypePlugIn(&lt;br /&gt;        DisplayName=&quot;Multiple dropdown&quot;, &lt;br /&gt;        Description=&quot;Show multiple dropdowns, values in second dropdown are dependent on selected values in first dropdown.&quot;)]&lt;br /&gt;    public class MultipleDropDownProperty: PropertyLongString&lt;br /&gt;    {&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Return MultipleDropdownChoices type to application code&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public override Type PropertyValueType&lt;br /&gt;        {&lt;br /&gt;            get&lt;br /&gt;            {&lt;br /&gt;                return typeof(MultipleDropdownChoices);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Internally, we use Json to serialize &amp;amp; deserialize MultipleDropdownChoices value object to &amp;amp; from string&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public override object Value&lt;br /&gt;        {&lt;br /&gt;            get&lt;br /&gt;            {&lt;br /&gt;                var value = base.Value as string;&lt;br /&gt;                if (value == null)&lt;br /&gt;                {&lt;br /&gt;                    return null;&lt;br /&gt;                }&lt;br /&gt;                return JsonConvert.DeserializeObject&amp;lt;MultipleDropdownChoices&amp;gt;(value);&lt;br /&gt;            }&lt;br /&gt;            set&lt;br /&gt;            {&lt;br /&gt;                if (value is MultipleDropdownChoices)&lt;br /&gt;                {&lt;br /&gt;                    base.Value = JsonConvert.SerializeObject(value);&lt;br /&gt;                }&lt;br /&gt;                else&lt;br /&gt;                {&lt;br /&gt;                    base.Value = value;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public override object SaveData(PropertyDataCollection properties)&lt;br /&gt;        {&lt;br /&gt;            //return a string object so that EPi can save it to database&lt;br /&gt;            return LongString;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.71429; margin-bottom: 1.71429rem;&quot;&gt;After this we should create a EditorDescriptor, which is used to tell EPiServer CMS that which Dojo widget will be used to edit our property in EPiServer CMS edit mode, it also used to send custom initialization parameters to our Dojo widget&lt;/div&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[EditorDescriptorRegistration(&lt;br /&gt;        EditorDescriptorBehavior= EditorDescriptorBehavior.Default, &lt;br /&gt;        TargetType= typeof(MultipleDropdownChoices),&lt;br /&gt;        UIHint=&quot;Dropdowns&quot;)]&lt;br /&gt;    public class MultipleDropdownEditorDescriptor: EditorDescriptor&lt;br /&gt;    {&lt;br /&gt;        public MultipleDropdownEditorDescriptor()&lt;br /&gt;        {&lt;br /&gt;            ClientEditingClass = &quot;multipledropdownproperty.scripts.editor&quot;;&lt;br /&gt;        }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;       protected override void SetEditorConfiguration(EPiServer.Shell.ObjectEditing.ExtendedMetadata metadata)&lt;br /&gt;        {&lt;br /&gt;            //we send some custom initialization parameters to our Dojo widget here!&lt;br /&gt;            EditorConfiguration[&quot;choice1StoreName&quot;] = &quot;multipledropdownproperty.firstchoicedropdownreststore&quot;;&lt;br /&gt;            EditorConfiguration[&quot;choice2StoreName&quot;] = &quot;multipledropdownproperty.secondchoicedropdownreststore&quot;;&lt;br /&gt;            base.SetEditorConfiguration(metadata);&lt;br /&gt;        }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We should set ClientEditingClass property to our Dojo widget class name, this class name follow physical folder structure to our Dojo widget&#39;s JS file. We should put all JS, and CSS files of our modules under &quot;ClientResources&quot; folder, we can customize structure below this folder. Let&#39;s create a JS file file &quot;editor.js&quot; for our Dojo widget and put it under &quot;ClientResources/MultipleDropDown/Scripts&quot; folder as image below.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-dxnwbYsXTII/Ve_b0ECcuNI/AAAAAAAAIUY/6MX10t-_BeM/s1600/img1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;160&quot; src=&quot;http://4.bp.blogspot.com/-dxnwbYsXTII/Ve_b0ECcuNI/AAAAAAAAIUY/6MX10t-_BeM/s320/img1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We can add an default Alloy site to our solution, called &quot;Sample&quot; site, when we deploy our custom property module to this Sample site, we can copy DLL of our module to Sample site bin folder (or add reference to our module to Sample site), and copy content under &quot;ClientResources&quot; folder of our module to&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;ClientResources&quot; folder of Sample site (we can use a post build command line to do this, such as :&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;xcopy /r /y /s &quot;$(SolutionDir)MultipleDropdownProperty\ClientResources\*.*&quot; &quot;$(SolutionDir)SampleSite\ClientResources\&quot; )&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&amp;nbsp;To make Dojo framework inside EPiServer CMS edit mode know where to look for JS, CSS files of our module, we have to edit module.config file of Sample site as below:&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt; &amp;lt;module&amp;gt;&lt;br /&gt;    &amp;lt;assemblies&amp;gt;&lt;br /&gt;      &amp;lt;add assembly=&quot;SampleSite&quot; /&amp;gt;&lt;br /&gt;      &amp;lt;!-- This adds the MultipleDropdownProperty assembly to the &quot;default module&quot; --&amp;gt;&lt;br /&gt;      &amp;lt;add assembly=&quot;MultipleDropdownProperty&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/assemblies&amp;gt;&lt;br /&gt;    &amp;lt;clientResources&amp;gt;&lt;br /&gt;        &amp;lt;add name=&quot;epi-cms.widgets.base&quot; path=&quot;Styles/Styles.css&quot; resourceType=&quot;Style&quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/clientResources&amp;gt;&lt;br /&gt;    &amp;lt;dojo&amp;gt;&lt;br /&gt;        &amp;lt;paths&amp;gt;&lt;br /&gt;          &amp;lt;add name=&quot;alloy&quot; path=&quot;Scripts&quot; /&amp;gt;&lt;br /&gt;          &amp;lt;!-- Add a mapping from module name &quot;multipledropdownproperty&quot; to &quot;MultipleDropDown&quot; sub folder of ~/ClientResources/ folder --&amp;gt;&lt;br /&gt;          &amp;lt;add name=&quot;multipledropdownproperty&quot; path=&quot;MultipleDropDown&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;/paths&amp;gt;&lt;br /&gt;    &amp;lt;/dojo&amp;gt;&lt;br /&gt; &amp;lt;/module&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We mapped module name &quot;&lt;/span&gt;multipledropdownproperty&quot; &lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;to a &quot;MultipleDropDown&quot; subfolder of &quot;ClientResources&quot; folder so from a class name&amp;nbsp;&lt;/span&gt;&quot;multipledropdownproperty.scripts.editor&quot;,&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&amp;nbsp;Dojo can see the first part is module name, find&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;MultipleDropDown&quot; folder,&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&amp;nbsp;second part is sub folder &quot;Scripts&quot; under&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;MultipleDropDown&quot;, and &quot;editor&quot; is the name our Dojo widget JS file.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;We have to add our custom property to a page type, as below:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; public class ArticlePage : StandardPage&lt;br /&gt;    {&lt;br /&gt;        [UIHint(&quot;Dropdowns&quot;)]&lt;br /&gt;        [BackingType(typeof(MultipleDropDownProperty))]&lt;br /&gt;        [Display(Name =&quot;Page with a specific category&quot;, Description =&quot;After select a category in first dropdown, we can select a page with that category in second dropdown&quot;)]&lt;br /&gt;        public virtual MultipleDropdownChoices DropDowns { get; set; }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;The UIHint attribute value must match that on EditorDscriptor class.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;Now let&#39;s try to add some content to out Dojo widget file &quot;editor.js&quot;:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;define([&lt;br /&gt;&quot;dojo/_base/declare&quot;,&lt;br /&gt;&quot;dojo/on&quot;,&lt;br /&gt;&quot;dojo/store/Memory&quot;,&lt;br /&gt;&quot;dijit/_WidgetBase&quot;,&lt;br /&gt;&quot;dijit/_TemplatedMixin&quot;,&lt;br /&gt;&quot;dijit/_WidgetsInTemplateMixin&quot;,&lt;br /&gt;&quot;dijit/form/FilteringSelect&quot;,&lt;br /&gt;&quot;epi-cms/_ContentContextMixin&quot;,&lt;br /&gt; &quot;epi/dependency&quot;&lt;br /&gt;], function (&lt;br /&gt;    declare,&lt;br /&gt;    on,&lt;br /&gt;    Memory,&lt;br /&gt;    _WidgetBase,&lt;br /&gt;    _TemplatedMixin,&lt;br /&gt;    _WidgetsInTemplateMixin,&lt;br /&gt;    FilteringSelect,&lt;br /&gt;    _ContentContextMixin,&lt;br /&gt;    dependency&lt;br /&gt;    ) {&lt;br /&gt;    return declare(&quot;multipledropdownproperty.scripts.editor&quot;,&lt;br /&gt;        [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _ContentContextMixin], {&lt;br /&gt;           &lt;br /&gt;            value: null,  //required for EPiServer CMS&lt;br /&gt;            templateString: &quot;Hello world!&quot;,&lt;br /&gt;            constructor: function (params) {&lt;br /&gt;                //&quot;params&quot; contained all settings sent from class MultipleDropdownEditorDescriptor at server side&lt;br /&gt;                //&quot;params&quot; will be mixed in after this constructor is call and before postMixInProperties is called&lt;br /&gt;            },&lt;br /&gt;            postMixInProperties: function () {&lt;br /&gt;                //called after constructor and after parameters are mixed in current widget instance&lt;br /&gt;                //before DOM nodes are created&lt;br /&gt;&lt;br /&gt;                //call the base implementation&lt;br /&gt;                this.inherited(arguments);&lt;br /&gt;            },&lt;br /&gt;            postCreate: function () {&lt;br /&gt;                //widgets has been render but not sub widgets in container node, &lt;br /&gt;                //not attached to DOM yet&lt;br /&gt;               &lt;br /&gt;                //call the base implementation&lt;br /&gt;                this.inherited(arguments);&lt;br /&gt;            },&lt;br /&gt;            startup: function () {&lt;br /&gt;                //all child widgets are created&lt;br /&gt;&lt;br /&gt;                //call the base implementation&lt;br /&gt;                this.inherited(arguments);&lt;br /&gt;            },&lt;br /&gt;            onChange: function (value) { }, //required for EPiServer CMS&lt;br /&gt;            onBlur: function (e) { },      //required for EPiServer CMS&lt;br /&gt;            onFocus: function (value) { }, //required for EPiServer CMS&lt;br /&gt;           &lt;br /&gt;            //custom setter for value attribute&lt;br /&gt;            _setValueAttr: function (value) {&lt;br /&gt;                //call the base so that can base widget can raise observable events.&lt;br /&gt;                this._set(&quot;value&quot;, value);&lt;br /&gt;            },&lt;br /&gt;            //custom getter for value attribute&lt;br /&gt;            _getValueAttr: function () {&lt;br /&gt;                return this.value;&lt;br /&gt;            },&lt;br /&gt;            //returns wether our module is valid or not, required for EPiServer CMS&lt;br /&gt;            isValid: function () {&lt;br /&gt;                return true;&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;Above script is just the skeleton for our Dojo widget, let&#39;s look at different parts in this skeleton code quickly:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;If you read this &lt;a href=&quot;https://dojotoolkit.org/documentation/tutorials/1.10/modules/&quot;&gt;link&lt;/a&gt;&amp;nbsp;you would understand define keyword used to define our module, using declare keyword to create a class named &quot;&lt;/span&gt;&lt;/span&gt;multipledropdownproperty.scripts.editor&quot; &lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;and it requires other modules like &quot;dojo/on&quot;, &quot;dojo/store/Memory&quot; etc..We will explore more referenced module along the way.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;if you read &lt;a href=&quot;http://dojotoolkit.org/reference-guide/1.10/dijit/_WidgetBase.html&quot;&gt;_WidgetBase&lt;/a&gt;, &lt;a href=&quot;http://dojotoolkit.org/reference-guide/1.10/quickstart/writingWidgets.html&quot;&gt;write Dojo widget&lt;/a&gt;, you would understand&amp;nbsp;&quot;dijit/_WidgetBase&quot;, &amp;nbsp;&quot;dijit/_TemplatedMixin&quot;, &amp;nbsp;&quot;dijit/_WidgetsInTemplateMixin&quot; module references. You will learn widget life cycle methods (constructor, postMixInProperties, postCreate, startup methods), mixin concept, learn about custom getter and setter for attribute, using templateString attribute to render our widget.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;You also can see that all setting values sent from server to our Dojo widget are available in &quot;params&quot; argument in constructor of the widget, this params also contain exising value, if any, of this property loaded from database. We usually do not do anything with this params argument in constructor because later it will be mixed in our widget, before postMinInProperties method is called.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;Inside our Dojo widget, there are some API that is needed to make EPiServer CMS (the CMS) edit mode understand and work with our widget:&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;value&quot; attribute: our Dojo widget is used to edit value of a property in CMS edit mode, so CMS get and set this property value through &quot;value&quot; attribute of the widget. But CMS always access this attribute through standard &quot;get&quot; and &quot;set&quot; methods of Dojo widget, so we can define custom getter &amp;amp; setter methods to customize they way CMS access this value.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;onchange&quot; event: this is also required by the CMS, used to notify CMS that our property value has changed&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;onFocus&quot; and &quot;onBlur&quot; events: required by the CMS, if our widget composed by more than one input fields (we have two dropdownlist). onFocus notify CMS that we start editing our property, and onBlur notify CMS that we complete editing our property. You can read source code of &quot;&lt;/span&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;epi-cms/contentediting/SideBySideEditorWrapper&quot; module to understand how CMS works with these events of our widget.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;OK, now let&#39;s add some editing functionalities into our Dojo widget. First we have to define how to render our widget, using templateString atrribute (supported by&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&amp;nbsp;&quot;dijit/_TemplatedMixin&quot; module), we can add following attributes to our widget&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; &lt;br /&gt; &lt;br /&gt; templateString: &quot;&amp;lt;div class=\&quot;dijitReset dijitInputField dijitInputContainer dijitInline epi-resourceInputContainer\&quot;&amp;gt;&quot; +&lt;br /&gt;                              &quot;&amp;lt;div&amp;gt;&amp;lt;span class=\&quot;dropdownLabel\&quot;&amp;gt;${choices1Label}: &amp;lt;span&amp;gt;&quot; +&lt;br /&gt;                                  &quot;&amp;lt;input class=\&quot;dropdown\&quot; data-dojo-type=\&quot;dijit/form/FilteringSelect\&quot; data-dojo-attach-point=\&quot;choices1\&quot;&amp;gt;&quot; +&lt;br /&gt;                              &quot;&amp;lt;/div&amp;gt;&quot; +&lt;br /&gt;                              &quot;&amp;lt;div&amp;gt;&amp;lt;span class=\&quot;dropdownLabel\&quot;&amp;gt;${choices2Label}: &amp;lt;span&amp;gt;&quot; +&lt;br /&gt;                                  &quot;&amp;lt;input  class=\&quot;dropdown\&quot; data-dojo-type=\&quot;dijit/form/FilteringSelect\&quot; data-dojo-attach-point=\&quot;choices2\&quot;&amp;gt;&quot; +&lt;br /&gt;                              &quot;&amp;lt;/div&amp;gt;&quot; +&lt;br /&gt;                          &quot;&amp;lt;/div&amp;gt;&quot;,&lt;br /&gt;choices1Label: &quot;Categories:&quot;, //label for first drop down&lt;br /&gt; choices2Label: &quot;Pages:&quot;, //label for second drop down&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;Above template uses two Dojo filteringSelect, so we reference &lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&quot;dijit/form/FilteringSelect&quot; module, to pre-load this module, avoid error&lt;i&gt; &quot;parser returned unfilled promise (probably waiting for module auto-load), unsupported by _WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation.&quot; &lt;/i&gt;We have also variable substitution in our template&amp;nbsp;&lt;/span&gt;&lt;/span&gt;${choices1Label},&amp;nbsp;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;so we have to define two more attributes&amp;nbsp;&lt;/span&gt;choices1Label, choices2Label&amp;nbsp;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;for our widget&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We have two Dojo filteringSelect now, let&#39;s bind some data to them, we can create two EPiServer reststore, one to retrieve all categories in the system, one to get all pages with a specified category, and excluding editing page. Y&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;ou should read&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://dojotoolkit.org/reference-guide/1.10/dojo/store.html&quot; style=&quot;font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;this&lt;/a&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&amp;nbsp;to understand the API of JsonRest and Memory store of Dojo, EPiServer reststore is a JsonRest wrapped with some more functions like Observable and Throttle.&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&amp;nbsp;We shall create two base classes as below:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;  &lt;br /&gt;    public abstract class FirstDropDownChoicesStoreBase : RestControllerBase&lt;br /&gt;    {&lt;br /&gt;        public virtual RestResult Get(int? id)&lt;br /&gt;        {&lt;br /&gt;            return base.Rest(GetChoices(id));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Should return all the choices for first drop down. &lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;br /&gt;        public abstract IEnumerable&amp;lt;Choicemodel&amp;gt; GetChoices(int? id);&lt;br /&gt;&lt;br /&gt;        public virtual RestResult Put(ChoiceModel entity)&lt;br /&gt;        {&lt;br /&gt;            throw new NotSupportedException();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public virtual RestResult Post(ChoiceModel entity)&lt;br /&gt;        {&lt;br /&gt;            throw new NotSupportedException();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public virtual RestResult Delete(int id)&lt;br /&gt;        {&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public abstract class SecondDropDownChoicesStoreBase : RestControllerBase&lt;br /&gt;    {&lt;br /&gt;        public virtual RestResult Get(string selectedFirstChoiceId, string currentContentId)&lt;br /&gt;        {&lt;br /&gt;            if (selectedFirstChoiceId != null &amp;amp;amp;&amp;amp;amp; currentContentId != null)&lt;br /&gt;                return base.Rest(GetChoices(selectedFirstChoiceId, currentContentId));&lt;br /&gt;            else&lt;br /&gt;                return base.Rest(new List&amp;lt;choicemodel&amp;gt;());&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Should return all the choices based on selected value of first drop down and current editing content &lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;br /&gt;        public abstract IEnumerable&amp;lt;Choicemodel&amp;gt; GetChoices(string selectedFirstChoiceId, string currentContentId);&lt;br /&gt;&lt;br /&gt;        public virtual RestResult Put(ChoiceModel entity)&lt;br /&gt;        {&lt;br /&gt;            throw new NotSupportedException();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public virtual RestResult Post(ChoiceModel entity)&lt;br /&gt;        {&lt;br /&gt;            throw new NotSupportedException();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public virtual RestResult Delete(int id)&lt;br /&gt;        {&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;Implementations of these base classes are also quite straight forward:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[RestStore(&quot;firstchoicedropdownreststore&quot;)]&lt;br /&gt;    public class FirstChoiceDropdownRestStore : FirstDropDownChoicesStoreBase&lt;br /&gt;    {&lt;br /&gt;        private readonly CategoryRepository _categoryRepository;&lt;br /&gt;        public FirstChoiceDropdownRestStore(CategoryRepository categoryRepository)&lt;br /&gt;        {&lt;br /&gt;            _categoryRepository = categoryRepository;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Build a list of categories, like &quot;cat1&quot;,&quot;cat1/sub11&quot;,&quot;cat2&quot;,&quot;cat2/sub21&quot;&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;param name=&quot;items&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;        /// &amp;lt;param name=&quot;parent&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;        /// &amp;lt;param name=&quot;prefix&quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;        private void BuildCategoryTree(List&amp;lt;ChoiceModel&amp;gt; items, Category parent, string prefix)&lt;br /&gt;        {&lt;br /&gt;            var newPrefix = (prefix ==string.Empty ? string.Empty: (prefix + &quot;/&quot;)) + parent.Name;&lt;br /&gt;            items.Add(new ChoiceModel() { Name = newPrefix, Id = parent.ID.ToString() });&lt;br /&gt;            &lt;br /&gt;            foreach (var cat in parent.Categories)&lt;br /&gt;            {&lt;br /&gt;                if (cat.Available &amp;amp;&amp;amp; cat.Selectable)&lt;br /&gt;                    BuildCategoryTree(items, cat, newPrefix);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public override IEnumerable&amp;lt;ChoiceModel&amp;gt; GetChoices(int? id)&lt;br /&gt;        {&lt;br /&gt;            var choicesList = new List&amp;lt;ChoiceModel&amp;gt;();&lt;br /&gt;            foreach (var cat in _categoryRepository.GetRoot().Categories)&lt;br /&gt;            {&lt;br /&gt;                BuildCategoryTree(choicesList, cat, string.Empty);&lt;br /&gt;            }&lt;br /&gt;            return choicesList;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;[RestStore(&quot;secondchoicedropdownreststore&quot;)]&lt;br /&gt;    public class SecondChoiceDropdownRestStore: SecondDropDownChoicesStoreBase&lt;br /&gt;    {&lt;br /&gt;        private CategoryRepository _categoryRepository;&lt;br /&gt;        private ContentSearchHandler _contentSearchHandler;&lt;br /&gt;        private TemplateResolver _templateResolver;&lt;br /&gt;        public SecondChoiceDropdownRestStore(CategoryRepository categoryRepository, &lt;br /&gt;            ContentSearchHandler contentSearchHandler,&lt;br /&gt;            TemplateResolver templateResolver)&lt;br /&gt;        {&lt;br /&gt;            _categoryRepository = categoryRepository;&lt;br /&gt;            _contentSearchHandler = contentSearchHandler;&lt;br /&gt;            _templateResolver = templateResolver;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;       &lt;br /&gt;        public override IEnumerable&amp;lt;ChoiceModel&amp;gt; GetChoices(string selectedFirstChoiceId, string currentContentId)&lt;br /&gt;        {&lt;br /&gt;            var catId = -1;&lt;br /&gt;            Category cat = null;&lt;br /&gt;            var currentContent = ContentReference.EmptyReference;&lt;br /&gt;            if (int.TryParse(selectedFirstChoiceId, out catId))&lt;br /&gt;            {&lt;br /&gt;                cat = _categoryRepository.Get(catId);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            if (catId == -1 || cat == null)&lt;br /&gt;            {&lt;br /&gt;                return new List&amp;lt;ChoiceModel&amp;gt;();&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            ContentReference.TryParse(currentContentId, out currentContent);&lt;br /&gt;&lt;br /&gt;            var resultList = new List&amp;lt;ChoiceModel&amp;gt;();&lt;br /&gt;&lt;br /&gt;            var searchHanler = ServiceLocator.Current.GetInstance&amp;lt;SearchHandler&amp;gt;();&lt;br /&gt;&lt;br /&gt;            var query = new CategoryQuery(LuceneOperator.OR);&lt;br /&gt;            query.Items.Add(cat.ID.ToString());&lt;br /&gt;&lt;br /&gt;            var results = searchHanler.GetSearchResults(query, 1, int.MaxValue);&lt;br /&gt;            resultList.AddRange(results.IndexResponseItems.SelectMany(CreateHitModel));&lt;br /&gt;&lt;br /&gt;            if (currentContent != ContentReference.EmptyReference)&lt;br /&gt;                resultList.RemoveAll(i =&amp;gt; i.Id == currentContent.ID.ToString());&lt;br /&gt;&lt;br /&gt;            return resultList;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private IEnumerable&amp;lt;ChoiceModel&amp;gt; CreateHitModel(IndexResponseItem responseItem)&lt;br /&gt;        {&lt;br /&gt;            var content = _contentSearchHandler.GetContent&amp;lt;IContent&amp;gt;(responseItem);&lt;br /&gt;            if (content != null &amp;amp;&amp;amp; HasTemplate(content) &amp;amp;&amp;amp; IsPublished(content as IVersionable))&lt;br /&gt;            {&lt;br /&gt;                yield return CreatePageHit(content);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private bool HasTemplate(IContent content)&lt;br /&gt;        {&lt;br /&gt;            return _templateResolver.HasTemplate(content, TemplateTypeCategories.Page);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private bool IsPublished(IVersionable content)&lt;br /&gt;        {&lt;br /&gt;            if (content == null)&lt;br /&gt;                return true;&lt;br /&gt;            return content.Status.HasFlag(VersionStatus.Published);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private ChoiceModel CreatePageHit(IContent content)&lt;br /&gt;        {&lt;br /&gt;            return new ChoiceModel&lt;br /&gt;            {&lt;br /&gt;                Name = content.Name,&lt;br /&gt;                Id = content.ContentLink.ID.ToString()&lt;br /&gt;            };&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;We use CategoryRepository, SearchHandler, CategoryQuery, ContentSearchHandler classes to do our job in above implementation, the code should be&amp;nbsp;self-explanatory.&amp;nbsp;We have two reststore on server side now, how to&amp;nbsp;make them to be available on the client side for our widget to use ? we should use a client initialization module for our module and register our stores there, like below:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;define([&lt;br /&gt;// Dojo&lt;br /&gt;    &quot;dojo&quot;,&lt;br /&gt;    &quot;dojo/_base/declare&quot;,&lt;br /&gt;//CMS&lt;br /&gt;    &quot;epi/_Module&quot;,&lt;br /&gt;    &quot;epi/dependency&quot;,&lt;br /&gt;    &quot;epi/routes&quot;&lt;br /&gt;], function (&lt;br /&gt;// Dojo&lt;br /&gt;    dojo,&lt;br /&gt;    declare,&lt;br /&gt;//CMS&lt;br /&gt;    _Module,&lt;br /&gt;    dependency,&lt;br /&gt;    routes&lt;br /&gt;) {&lt;br /&gt;&lt;br /&gt;    return declare(&quot;multipledropdownproperty.scripts.moduleinitializer&quot;, [_Module], {&lt;br /&gt;        // summary: Module initializer for the MultipleDropdownProperty module.&lt;br /&gt;&lt;br /&gt;        initialize: function () {&lt;br /&gt;&lt;br /&gt;            this.inherited(arguments);&lt;br /&gt;&lt;br /&gt;            var registry = this.resolveDependency(&quot;epi.storeregistry&quot;);&lt;br /&gt;&lt;br /&gt;            //Register the stores&lt;br /&gt;            var firstStore = registry.create(&quot;multipledropdownproperty.firstchoicedropdownreststore&quot;, this._getRestPath(&quot;firstchoicedropdownreststore&quot;));&lt;br /&gt;            &lt;br /&gt;            var secondStore = registry.create(&quot;multipledropdownproperty.secondchoicedropdownreststore&quot;, this._getRestPath(&quot;secondchoicedropdownreststore&quot;));&lt;br /&gt;        },&lt;br /&gt;&lt;br /&gt;        _getRestPath: function (name) {&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;            //we use the default module area named &quot;app&quot; of EPiServer Shell module&lt;br /&gt;            return routes.getRestPath({ moduleArea: &quot;app&quot;, storeName: name });&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We put this ModuleInitializer.js in same folder with editor.js, then we have to modify module.config this time to tell CMS to load this initilizer after the default CMS module, we also create a simple CSS file for our module under &quot;ClientResources/MultipleDropDown/Styles/styles.css&quot; and register this in module.config. Final module.config look like:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;br /&gt;&amp;lt;module&amp;gt;&lt;br /&gt;    &amp;lt;assemblies&amp;gt;&lt;br /&gt;      &amp;lt;add assembly=&quot;SampleSite&quot; /&amp;gt;&lt;br /&gt;      &amp;lt;!-- This adds the MultipleDropdownProperty assembly to the &quot;default module&quot; --&amp;gt;&lt;br /&gt;      &amp;lt;add assembly=&quot;MultipleDropdownProperty&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/assemblies&amp;gt;&lt;br /&gt;    &amp;lt;clientResources&amp;gt;&lt;br /&gt;      &amp;lt;add name=&quot;epi-cms.widgets.base&quot; path=&quot;Styles/Styles.css&quot; resourceType=&quot;Style&quot;/&amp;gt;&lt;br /&gt;      &amp;lt;add name=&quot;multipledropdownpropertystyle&quot; path=&quot;MultipleDropDown/styles/styles.css&quot; resourceType=&quot;Style&quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/clientResources&amp;gt;&lt;br /&gt;    &amp;lt;dojo&amp;gt;&lt;br /&gt;        &amp;lt;paths&amp;gt;&lt;br /&gt;          &amp;lt;add name=&quot;alloy&quot; path=&quot;Scripts&quot; /&amp;gt;&lt;br /&gt;          &amp;lt;!-- Add a mapping from module name &quot;multipledropdownproperty&quot; to &quot;MultipleDropDown&quot; sub folder of ~/ClientResources/ folder --&amp;gt;&lt;br /&gt;          &amp;lt;add name=&quot;multipledropdownproperty&quot; path=&quot;MultipleDropDown&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;/paths&amp;gt;&lt;br /&gt;    &amp;lt;/dojo&amp;gt;&lt;br /&gt;  &amp;lt;clientModule initializer=&quot;multipledropdownproperty.scripts.moduleinitializer&quot;&amp;gt;&lt;br /&gt;    &amp;lt;moduleDependencies&amp;gt;&lt;br /&gt;      &amp;lt;add dependency=&quot;CMS&quot; type=&quot;RunAfter&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/moduleDependencies&amp;gt;&lt;br /&gt;    &amp;lt;requiredResources&amp;gt;&lt;br /&gt;      &amp;lt;add name=&quot;multipledropdownpropertystyle&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/requiredResources&amp;gt;&lt;br /&gt;  &amp;lt;/clientModule&amp;gt;&lt;br /&gt;&amp;lt;/module&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;Now we get references to the stores in our widget and start using them in our Dojo widget:&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;postMixInProperties: function () {&lt;br /&gt;                //called after constructor and after parameters are mixed in current widget instance&lt;br /&gt;                //before DOM nodes are created&lt;br /&gt;&lt;br /&gt;                //call the base implementation&lt;br /&gt;                this.inherited(arguments);&lt;br /&gt;&lt;br /&gt;                //get setting values from server&lt;br /&gt;                if (this.params.choice1StoreName) {&lt;br /&gt;                    this.choices1StoreName = this.params.choice1StoreName;&lt;br /&gt;                    var registry = dependency.resolve(&quot;epi.storeregistry&quot;);&lt;br /&gt;                    this.choices1Store = registry.get(this.choices1StoreName);&lt;br /&gt;                }&lt;br /&gt;                if (this.params.choice2StoreName) {&lt;br /&gt;                    this.choices2StoreName = this.params.choice2StoreName;&lt;br /&gt;                    var registry = dependency.resolve(&quot;epi.storeregistry&quot;);&lt;br /&gt;                    this.choices2Store = registry.get(this.choices2StoreName);&lt;br /&gt;                }&lt;br /&gt;            },&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;In postCreate method, the dropdowns are available so we actually bind the stores to the dropdowns:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;postCreate: function () {&lt;br /&gt;                //widgets has been render but not attached to DOM yet&lt;br /&gt;&lt;br /&gt;                this.choices1.store = this.choices1Store;&lt;br /&gt;                var widget = this;&lt;br /&gt;                &lt;br /&gt;                //we query all items of the store from server side (probably take some time)&lt;br /&gt;                //then data bind the first dropdown,&lt;br /&gt;                this.choices1.store.query().then(function () {&lt;br /&gt;&lt;br /&gt;                    //data bind the first dropdown&lt;br /&gt;                    widget.choices1.startup();&lt;br /&gt;                    var currentValue = widget.value;&lt;br /&gt;&lt;br /&gt;                    if (currentValue.choice1.id &amp;amp;&amp;amp; widget.choices1) {&lt;br /&gt;                        widget.choices1.set(&#39;value&#39;, currentValue.choice1.id);&lt;br /&gt;&lt;br /&gt;                        if (currentValue.choice2.id &amp;amp;&amp;amp; widget.choices2) {&lt;br /&gt;                            //query data for second dropdown based on first selection, then bind this data to second dropdown&lt;br /&gt;                            widget._setupStoreForSecondDropdown(currentValue.choice1.id, currentValue.choice2.id);&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                });&lt;br /&gt;               &lt;br /&gt;                //call the base implementation&lt;br /&gt;                this.inherited(arguments);&lt;br /&gt;            },&lt;br /&gt; _setupStoreForSecondDropdown: function (selectedFirstChoiceId, selectedId) {&lt;br /&gt;                var data = [];&lt;br /&gt;                var widget = this;&lt;br /&gt;                var firstItem = null;&lt;br /&gt;                var firstChoiceId = selectedFirstChoiceId;&lt;br /&gt;                var sId = selectedId;&lt;br /&gt;&lt;br /&gt;                //query data for second dropdown based on first selection, then bind this data to second dropdown&lt;br /&gt;                this.choices2Store.query({ selectedFirstChoiceId: firstChoiceId, currentContentId: this._currentContext.id }).then(function (results) {&lt;br /&gt;                    results.forEach(function (item, i) {&lt;br /&gt;                        data.push(item);&lt;br /&gt;                        if (i == 0) firstItem = item;&lt;br /&gt;                    });&lt;br /&gt;&lt;br /&gt;                    if (firstItem != null) {&lt;br /&gt;&lt;br /&gt;                        //after querying to get items from server side, put them in a&lt;br /&gt;                        //memory store to bind to second dropdown&lt;br /&gt;                        var store2 = new Memory({ data: data });&lt;br /&gt;                        widget.choices2.store = store2;&lt;br /&gt;                        widget.choices2.startup();&lt;br /&gt;&lt;br /&gt;                        var id = sId ? sId : firstItem.id;&lt;br /&gt;&lt;br /&gt;                        //set first item or selected item from param to be selected!&lt;br /&gt;                        if (!widget.choices2.get(&#39;value&#39;)) {&lt;br /&gt;                            widget.choices2.set(&#39;value&#39;, id);&lt;br /&gt;                        }&lt;br /&gt;                    } else {&lt;br /&gt;                        //if found no items from server for 2nd dropdown&lt;br /&gt;                        widget._secondDropdownChanged();&lt;br /&gt;                    }&lt;br /&gt;                });&lt;br /&gt;            },&lt;span style=&quot;font-family: Times New Roman;&quot;&gt;&lt;span style=&quot;white-space: normal;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;When we mixin &quot;_ContentContextMixin &quot; module into our widget, we can access the current editing content in our widget like this&amp;nbsp;&lt;/span&gt;this._currentContext.id&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We have more code in Dojo widget to handle focus, blur, raise onChange event. You can download the full source code of this project here:&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;https://github.com/hoang-tranminh/MultipleDropdownProperty. You can login to edit mode by account &quot;hoang/Abc123456&quot; when running my project.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #444444; font-family: Open Sans, Helvetica, Arial, sans-serif;&quot;&gt;&lt;span style=&quot;font-size: 14px; line-height: 24.0001px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #444444; font-family: &#39;Open Sans&#39;, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 24.0001px;&quot;&gt;We can extend this example using PropertySettings to send setting value to our Dojo widget, to support on page editing etc. Thanks for reading.&lt;/span&gt;</description>            <guid>http://hoang-tranminh.blogspot.com/2015/09/linked-dropdownlists-custom-property-in.html</guid>            <pubDate>Wed, 09 Sep 2015 13:13:00 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>