Thursday, April 25, 2013

Combining CMS and e-commerce in the best way


Customers are aware of that they need to MVT-test checkout flows, shopping basket placement as well as have a slick interface for WYSIWYG adding and changing product information.

But what else is there to be done?

Burberry, working with a high profile line of fashion products, showed an great example of experience driven commerce during the Adobe Summit in London 2013. They have coined their project "Bring the Online Experience to Stores, and Not Vice Versa".

They have worked hard to build the next generation of beautiful product catalogues on their integrated Adobe CQ5 and hybris e-commerce solution. The idea was that product listings on the web are linear and ugly and product catalogues can be really inspiring pieces of art.



Their partner, new web firm Deloitte Digital with project manager Andrew Clarson, combined the CMS and e-commerce solutions so that the editors got the possibility to combine DAM such as fashion show reels with e-commerce parts (product areas). The CMS then had a scripted frontendcode solution so i.e. imagemap areas could be spotted, marked and by linking product data immediately turned into e-commerce parts. Generating predefined e-commerce product parts. Shown as “products in this show”, directly beneath a fashion show catwalk.

A really nice way to continuously give marketers and product managers the possibility to convert on more than just boring, classic product listings.

Monday, April 15, 2013

Don't Miss Sprint Retrospectives!

It may seem just to skip Sprint Retrospectives, especially when there is “work to be done,” but like all scrum milestones retrospectives must be executed to realize the maximum value of the scrum framework. They contribute to self-organization, team building, reenergizing, and the socialization of issues.

Scrum is a framework for managing chaos, and sprint retrospectives, an empirical process improvement tool, improve the management of chaos. Scrum founding member Mike Beedle explains, “the purpose of [the sprint retrospective] is to improve the development process, and make it more effective and enjoyable for the next sprint. The dimensions of what went right or wrong are people, relationships, process and tools.”

Sprint Retrospectives spark insight, and have an enlightening atmosphere. Perhaps it's the basking in successes of the just finished sprint, the chance of lifting the weight of issues that have been bothering team members, recalling “Ah-Ha” moments, or strengthening of the process overall.

After the 10th retrospective its understandable if feel all the benefits of the sprint retrospective have been acquired. However, chances are even if you’re unenthusiastic about the retrospective, one of your team members has been waiting for this scheduled opportunity to share their thoughts. It is possible that your retrospectives decrease in duration over time, but each sprint retrospective is unique. Changing team dynamics, events, projects, stages of a project, and experiences contribute to the evolution of sprint retrospective benefits over time.

Would you like your team to get more done? Take sprint retrospective time to enjoy space, relax, eat pizza (drink beers), joke around, and reenergize. “We cannot add hours to our day, but we can add energy,” explain Tony Schwartz and Catherine McCarthy of the energy project. Retrospectives provide breathing room for teams who have been working hard. “One of the best ways of adding energy is increasing passion,” explains Mike Cohn in Succeeding with Agile. “The more passionate people are about their projects, the more likely they are to fully engage on them each day.” Increase teams’ passion by providing ownership through retrospectives. All team members have the opportunity to share successes, address issues, give praise, identify ah-ha moments, and provide hypotheses for the next sprint.

At best cutting corners (skipping meetings) of the scrum process leads to bleeding of benefits, at worst, disorientation and room for chaos (in its many forms) to take the reigns of your project. Every scrum milestone contributes to organizing and reinforcing the social fabric of the team. Don’t skip sprint retrospectives!

P.S. If you’re using Trello (amazing tool!) for managing projects, you may find this Sprint Retrospective Template useful!

Wednesday, April 10, 2013

Blank edit after upgrading to EPiServer 7.1

After installing the upgrade you are supposed to restart your application to finish the upgrade process. What happened was that the whole edit window was totally blank. Well not totally I could see the colored edit bar, and everything that is rendered wen it's expanded. Admin mode also worked. The problem was not related to the Preamble property error that Erik pointed out.
The way to resolve this was to empty the ASP.NET temporary files folder (located at C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root  or Framework64), do an iisreset and start the site.

This is one of the things to take into account when deploying an upgrade to your different environments.

By the way the folders affected by the update are:

  • webroot/modulesbin
  • yourVPPFolder/Modules
  • yourVPPFolder/ModulesRepository

Thursday, April 4, 2013

Nansen at An Event Apart Seattle: 2013

Being my first contribution to the esteemed Nansen blog, allow me to introduce myself: I'm Justin Dauer, Creative Director for our Chicago office. This past Monday, our Interface Architect Patrick Waks and I had the opportunity to attend An Event Apart in Seattle.


Nansen sponsors An Event Apart in Seattle


An Event Apart swag

Not only was this an incredible chance to absorb knowledge from thought leaders in our digital industry, but it was particularly sweet as Nansen was an official conference sponsor.


Nansen sponsors An Event Apart in Seattle


The Seattle conference is particularly intimate in terms of venue size, and as such, everything was just that much more accessible. Not only are you surrounded by "your people" in terms of industry peers, but the speakers themselves — titans of thought leadership and shapers of our industry — are equally as accessible. I had the opportunity to chat with Jeffrey Zeldman about the glory days of Mac OS 7-8 pixel-based icon design, the core benefit of sketching and thumbnailing with Jason Santa Maria, and making content and design open and accessible for all with Ethan Marcotte. It was particularly thrilling to see Nansen's design and implementation of the Coop web site be a part of Ethan's presentation Responsive and Responsible as an example of a beacon of responsive design. Not bad, coming from the very person who was the first to coin the phrase "responsive design".

The conference sessions themselves were a one-track format, with 12 speakers presenting over the course of a couple days, and a workshop session run by Luke Wroblewski the final day. While we're confident and passionate about functional creativity, a little validation never hurts, and seeing core facets of our process referenced and lauded again and again across multiple presentations certainly was great to behold. That, coupled with seeing where our industry is headed visually and technically, made for an incredibly rewarding experience.

My one regret would have been not having the chance to see more of the great city of Seattle. We were able to try a few of the local gastro pubs and watering holes, and Patrick and I enjoyed plenty of good conversation and equally good alcohol during the first day's after party at Buckley's in Belltown. I'll close out with a view from the Bell Harbor Convention Center's waterfront, and an invitation to come see us from Nansen as we again sponsor the An Event Apart conference in August – this time right in the backyard of our Chicago office.


the sun sets on Seattle

Scheduled Jobs not running after site upgrade

After an upgrade of a customer site from EPiServer 5 to EPiServer 6 all scheduled jobs stopped working in all environments. When debugging the scheduling service (in a command prompt run "EPiServer.SchedulerSvc.exe DEBUG" in the service directory) we noticed that when the service tried to connect to the site it failed with the following error message,


#INF# [_LM_W3SVC_1_ROOT] Failed calling site (Attempting to deserialize an empty stream.)

We tried to clear the "EPiServer.SchedulerService.Sites.xml" file to force a re-registration of the site but it never registered. After some thinking and contact with EPiServer support we found the fault, a missing module in Web.config. When we added the "FirstBeginRequestModule" module to the modules section everything started working again.

<configuration>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="FirstBeginRequestModule" precondition="managedHandler" type="EPiServer.Web.InitializationModule, EPiServer">
    </modules>
  </system.webServer>
</configuration>

Wednesday, April 3, 2013

How to disable CMS edit/admin mode on "public facing" servers in a load balanced environment

Update: some people have pointed out that they prefer the method of disabling the specific virtual paths in episerver.config.  See the end of this blog post for more information.

Hi Folks!

I realize that there have been several other blog posts regarding this topic, but after lots or trial and error I believe I have found a solid approach for disabling CMS edit/admin mode on public facing IIS servers in a load balanced environment.

There are other techniques that can be used to secure CMS edit/admin modes in a single server environment.  Check out this blog post by David Knipe for additional information.  It essentially uses IP white lists at the IIS level.  Maintaining IP whitelists can be problematic, so I prefer having a dedicated CMS server that's only accessible within the internal network.

In my last blog post we covered how to setup EPiServer load balancing using net.tcp.  We also detailed the three basic models that can be used when implementing EPiServer load balancing.  Today we'll be focusing on the Security model.

To recap, the security model provides a dedicated server for CMS editors to connect to and make changes to CMS content.  This server is normally only accessible from within the company's internal network.  The one or more "public facing" servers are what the general public connects to.  These "public facing" servers have the CMS edit/admin mode completely disabled.



For example:

Let's say we have our CMS server available onon http://cms.mysite.com (internal network only), and our "public facing" server available on http://www.mysite.com, then...

  • http://cms.mysite.com/episerver would prompt for login, and if the credentials are correct, allow the individual to navigate to CMS edit/admin mode.
  • http://www.mysite.com/episerver would be completely disabled, even if the user managed to steal a CMS editor's credentials.
Some of my Swedish colleagues have told me that this sort of setup is not very common in Sweden.  But I've personally noticed that many of our clients here in the States prefer having a dedicated CMS editor server.  I can't say I blame them.

So how do we harden the "public facing" servers.  It's remarkably easy.  All you have to do is make some tweaks to the Web.config file on the public facing server(s).

Disclaimer: these instructions work for EPiServer CMS 6 R2, I'm not sure about EPiServer CMS 7, but I believe the premise is essentially the same thing.

Web.config changes

The idea here is to modify any <location> element in the Web.config which currently requires WebEditorsWebAdmins or Administrators role membership to access.

For example, the <location> element for "/episerver" access by default looks like:

<location path="episerver">
  <system.web>
    <httpRuntime maxRequestLength="1000000" />
    <pages enableEventValidation="true">
      <controls>
        <add tagPrefix="EPiServerUI" namespace="EPiServer.UI.WebControls" assembly="EPiServer.UI" />
        <add tagPrefix="EPiServerScript" namespace="EPiServer.ClientScript.WebControls" assembly="EPiServer" />
        <add tagPrefix="EPiServerScript" namespace="EPiServer.UI.ClientScript.WebControls" assembly="EPiServer.UI" />
      </controls>
    </pages>
    <globalization requestEncoding="utf-8" responseEncoding="utf-8" />
    <authorization>
      <allow roles="WebEditors, WebAdmins, Administrators" />
      <deny users="*" />
    </authorization>
  </system.web>
  <system.webServer>
    <handlers>
      <clear />
      <add name="webresources" path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" />
      <add name="PageHandlerFactory-Integrated" path="*.aspx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" modules="ManagedPipelineHandler" scriptProcessor="" resourceType="Unspecified" requireAccess="Script" allowPathInfo="false" preCondition="integratedMode" responseBufferLimit="4194304" />
      <add name="SimpleHandlerFactory-Integrated" path="*.ashx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory" modules="ManagedPipelineHandler" scriptProcessor="" resourceType="Unspecified" requireAccess="Script" allowPathInfo="false" preCondition="integratedMode" responseBufferLimit="4194304" />
      <add name="WebServiceHandlerFactory-Integrated" path="*.asmx" verb="GET,HEAD,POST,DEBUG" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" modules="ManagedPipelineHandler" scriptProcessor="" resourceType="Unspecified" requireAccess="Script" allowPathInfo="false" preCondition="integratedMode" responseBufferLimit="4194304" />
      <add name="wildcard" path="*" verb="*" type="EPiServer.Web.StaticFileHandler, EPiServer" />
    </handlers>
  </system.webServer>
</location>

Notice that the <allow> element currently specifies that WebEditors, WebAdmins, and Administrators can access this location.  The <deny> elements tells IIS to deny everyone else.

All we essentially have to do is remove the <allow> element (comment it out) on the public facing servers and this location will be denied to everyone - including CMS editors/admins.  This means even if someone managed to steal the username and password of a CMS editor, they wouldn't be able to login to CMS edit mode unless they were somehow connected to the internal network and knew the URL to the CMS web application instance.

Please note: you can easily use Visual Studio transforms to create a Solution Configuration which automatically removes the <allow> element when publishing to the "public facing" servers.  We'll take a closer look at transforms in my next blog post.

Here we've commented out the <allow> element for the <location path="episerver"> element.

<location path="episerver">
  <system.web>
    <httpRuntime maxRequestLength="1000000" />
    <pages enableEventValidation="true">
      <controls>
        <add tagPrefix="EPiServerUI" namespace="EPiServer.UI.WebControls" assembly="EPiServer.UI" />
        <add tagPrefix="EPiServerScript" namespace="EPiServer.ClientScript.WebControls" assembly="EPiServer" />
        <add tagPrefix="EPiServerScript" namespace="EPiServer.UI.ClientScript.WebControls" assembly="EPiServer.UI" />
      </controls>
    </pages>
    <globalization requestEncoding="utf-8" responseEncoding="utf-8" />
    <authorization>
      <!--<allow roles="WebEditors, WebAdmins, Administrators" />-->
      <deny users="*" />
    </authorization>
  </system.web>
  <system.webServer>
    <handlers>
      <clear />
      <add name="webresources" path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" />
      <add name="PageHandlerFactory-Integrated" path="*.aspx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" modules="ManagedPipelineHandler" scriptProcessor="" resourceType="Unspecified" requireAccess="Script" allowPathInfo="false" preCondition="integratedMode" responseBufferLimit="4194304" />
      <add name="SimpleHandlerFactory-Integrated" path="*.ashx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory" modules="ManagedPipelineHandler" scriptProcessor="" resourceType="Unspecified" requireAccess="Script" allowPathInfo="false" preCondition="integratedMode" responseBufferLimit="4194304" />
      <add name="WebServiceHandlerFactory-Integrated" path="*.asmx" verb="GET,HEAD,POST,DEBUG" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" modules="ManagedPipelineHandler" scriptProcessor="" resourceType="Unspecified" requireAccess="Script" allowPathInfo="false" preCondition="integratedMode" responseBufferLimit="4194304" />
      <add name="wildcard" path="*" verb="*" type="EPiServer.Web.StaticFileHandler, EPiServer" />
    </handlers>
  </system.webServer>
</location>

You should repeat this step for all <location> objects that currently require WebEditors, WebAdmins, or Adminstrators role membership.

Depending on whether or not your using Commerce and/or Composer you might have to make the changes to the <location> elements with the following paths:

  • <location path="episerver">
  • <location path="episerver/CMS/admin" >
  • <location path="WebServices">
  • <location path="Admin/SettingsPlugin">
  • <location path="Admin/SitePlugin">
  • <location path="Edit">
  • <location path="EPiServerCommon">
  • <location path="dropit/plugin/extension/ui/edit">
  • <location path="dropit/plugin/extension/ui/admin">
After doing this, you'll notice one serious and annoying problem...




Assuming someone is able to login using CMS editor credentials, they can still access the EPiServer context menu (for on page editing), even if they cannot get to CMS edit/admin mode.

This is because the <location> elements don't affect access to the context menu.  The context menu is driven by EPiServer ACLs.

I stumbled across the solution on this blog post by Paul Houghton.  It requires building some code which will disable the EPiServer context menus for all page templates and then using appSettings to control whether or not this code should execute.  You could then configure the appSettings in Web.config for the servers where you want to disable the context menu appropriately.

Well appSettings was fine and dandy several years ago, but these days we use project level application settings.  See this post on Stackoverflow for addition information on the differences between appSettings and application settings.

So let's build a boolean application setting for our project in Visual Studio.



Now we can add some code in PageBase.cs (which all of our EPiServer templates inherit from) to override the onInit() page life cycle method.  This code checks the application settings value and determines if it should enable or disable the EPiServer context menu.

namespace MySite.Core
{
    public abstract class PageBase<T> : TemplatePage<T> where T : PageTypeBase
    {
        protected override void OnInit(System.EventArgs e)
        {
            if (ContextMenu != null && !Settings.Default.EnableEPiContextMenu)
            {
                ContextMenu.IsMenuEnabled = false;
            }

            base.OnInit(e);
        }
    }
}

The great part about application settings is that the values are compiled into the DLL, but can be overriden via the Web.config.  If you don't supply a value in Web.config, the compiled in value is used.  Pretty cool huh?

So now all we have to do is override the "True" value for our EnableEPiContextMenu in the Web.config on our public facing servers.

<applicationSettings>
  <MySite.Core.Properties.Settings>
    <setting name="EnableEPiContextMenu" serializeAs="String">
      <value>False</value>
    </setting>
  </MySite.Core.Properties.Settings>
</applicationSettings>

Extra steps for sites that don't require login functionality

If your site literally does not require any sort of login functionality for the general public you can further secure your "public facing" severs by disabling the login page completely.  You would keep the login page for the CMS server so that your CMS editors can login.

If you go this route, a person from the general public going to http://www.mysite.com/episerver would get a 404.  This is because:

  1. IIS determines the user is going to a location path that requires login.
  2. IIS tries to respond with the login page defined in the <forms> element in Web.config.
  3. IIS can't find the login page and therefore returns a 404.
If you are using the standard EPiServer login page, you can comment out this line in episerver.config.  This disables the virtual path to "~/Util" where the EPiServer login page is stored.

<virtualPath customFileSummary="~/FileSummary.config">
    <providers>
        ... more ...    
        
          
        <add name="App_Themes_Default" virtualPath="~/App_Themes/Default/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\App_Themes\Default"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />
            
        <add name="UI" virtualPath="~/episerver/CMS/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\UI\CMS"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />
            
        <!--<add name="UtilFiles" virtualPath="~/Util/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\util"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />-->

        ... more ...

    </providers>
</virtualPath>

If you are using a custom login page, just remove it from the build you publish to your "public" facing servers.

That about does it folks.  Tune in next time for how to use Visual Studio transforms to easily customize Visual Studio publish configurations.  This allows you to make configuration changes automatically when publishing builds of your web application to different servers: DEV, TEST, CMS, and PROD.

Thanks!

Rob

Update: As others have pointed out, another approach you can use is to remove the relevant virtual paths in episerver.config.  This approach will return a 404 when trying to access a resource that you don't want the general public to have access to.  This is because IIS literally can't find the resources (you disconnect the virtual link that points to the C:\Program Files\ directory on the file system).

I like the <location> element approach I outlined above works directly with IIS security, and I find it very simple to implement because it's easy to identify which <location> elements to modify.  But this approach is "closer to the metal" in that a malicious individual literally is unable to access the appropriate resources from the file system.

To implement the episerver.config virtual path approach, simply comment out the relevant virtual paths in episerver.config.

<!-- These virtual paths are commented out on 'public facing' server
to prevent access to CMS edit/admin mode

<add name="App_Themes_Default" virtualPath="~/App_Themes/Default/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\App_Themes\Default"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />

<add name="UI" virtualPath="~/episerver/CMS/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\UI\CMS"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />

<add name="UtilFiles" virtualPath="~/Util/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\util"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />

<add name="WebServiceFiles" virtualPath="~/WebServices/"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\webservices"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />

<add name="EPiServerCMS" virtualPath="~/episerver/CMS"
physicalPath="C:\Program Files (x86)\EPiServer\CMS\6.1.379.0\application\UI\EPiServer\CMS"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />

<add name="EPiServerShell" virtualPath="~/episerver/Shell"
physicalPath="C:\Program Files (x86)\EPiServer\Framework\6.2.267.1\Application\UI"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />

<add name="EPiServerUrlMappingVPP" virtualName="ExtensionMapping" virtualPath="~/episerver/CMS/"
bypassAccessCheck="false" showInFileManager="false" type="EPiServer.Web.Hosting.VirtualPathMappedProvider,EPiServer" />

<add name="EPiServerCommon" virtualPath="~/EPiServerCommon"
physicalPath="C:\Program Files (x86)\EPiServer\CommonFramework\4.1.517.380\Application\EPiServerCommon"
type="EPiServer.Web.Hosting.VirtualPathNonUnifiedProvider,EPiServer" />
-->


Monday, April 1, 2013

How to Configure EPiServer Load Balancing with net.tcp

Update: See the note at the bottom of this post in regards to storing custom data in the EPiServer cache.

EPiServer load balancing configuration is critical for large scale enterprise deployments. It allows you to have multiple IIS servers that all connect to same database.

There are three main models that can be used when using EPiServer load balancing.

1. Performance – When most people hear the term "load balancing" they think of the performance benefits that can be obtained. Using the performance model you will be splitting the incoming connections up between two or more IIS servers – each one running a copy of the web application. Using this model requires a minimum of two IIS servers. A hardware load balancer is normally also involved.



2. Security – Most people don’t think of using EPiServer load balancing to help with security, but I’ve found this is actually one of the primary benefits of load balancing. The idea here is to have one dedicated CMS IIS server, and one or more public facing IIS servers. The CMS IIS Server is normally only accessible via the company’s internal network. The public facing server(s) have the CMS edit/admin mode completely disabled. This model requires a minimum of two IIS servers.



3. Performance and Security – A hybrid of the two above models, requires a minimum of three IIS servers.



Note that technically you can also performance load balance the CMS IIS severs, but this is overkill in most cases. Unless of course you have an army of CMS editors maintaining the site.

There are already other blog posts on how to setup EPiServer load balancing using the preferred method, UDP Multicasting. I would be happy to create another blog post of this if there is demand, but the focus of this blog post is how to setup EPiServer load balancing when UPD Multicasting is not an option. In this case you need to use net.tcp.

I've had the privilege of setting up all three of the above models with both UDP multicast load balancing and net.tcp load balancing. You should always try UDP multicasting first, because the configuration is far easier. If you discover (to your horror) that UPD MC is not an option, you’ll have to bite the bullet, have a strong drink, and go the net.tcp route. Here’s a picture of me after I discovered that UDP multicasting wasn’t an option in our latest project.



Disclaimer: this individual isn't actually me, many of my fellow colleagues would never speak to me again if I started doing .NET development on an iMac. No second mouse button, give me a break!

EPiServer load balancing revolves around the broadcast of events to each of the servers. The idea is that if someone publishes a change to any of the pages on one server, the other servers must be told of this change and flush their cache (for the page that has changed). If you don’t have load balancing setup correctly, the other servers will continue to use the old, cached version of the page.

The key difference between UDP MC and using net.tcp is that UPD MC broadcasts events to all the servers listing a single multicast IP address and port. Because it’s a multicast IP address, all servers can receive the broadcast message.

Typically what prevents you from using UDP MC event handling are stricter security rules. Some IT departments don’t like the idea of UDP MC broadcasts being sent out to many servers and prefer to keep things more locked down.

So without further delay, let’s get started.

EPiServer.config Configuration

The first thing you need to do is update your episerver.config file so that each site has event handling turned on. There are two attributes that are required in the <siteSettings> element. These attributes are enableEvents and enableRemoteEvents. Both of these should be set to "true". If you have an EPiServer enterprise environment, you must do this for each <siteSettings> element for each <site>.

<sites>
  <site siteId="MySite" description="My Test Site">
    <siteSettings ... more stuff ... enableEvents="true" enableRemoteEvents="true" />
  </site>
  <site siteId="MySite2" description="My Test Site 2">
    <siteSettings ... more stuff ... enableEvents="true" enableRemoteEvents="true" />
  </site>
        
  ... more ...
 
</sites>

Now comes the fun part. You need to determine which servers will be event "broadcasters" and which servers will be event "listeners". Please note that servers can be both a "broadcaster" and a "listener".

Servers that are "broadcasters" will be broadcasting events to other servers. "Broadcasters" are typically the CMS server(s). If you have more than one server that CMS editors will be connecting to in order to publish changes you will have multiple "broadcasters".

Servers that are "listeners" will be receiving events from other servers. "Listeners" are typically the public facing servers. Please note that if a server is not configured to be a "listener" it will never be alerted if changes to content are made on other servers.

Net.tcp General Configuration

First you want to make sure that you have the appropriate configuration in Web.config to support net.tcp binding. Make sure that you have the <netTcpBinding> element in the <system.serviceModel><bindings> element in your Web.config.

<system.serviceModel>

    <client>
      ... more stuff ...
    </client>

    <bindings>

        <customBinding>
          <binding name="RemoteEventsBinding">
            <binaryMessageEncoding />
            <udpTransport multicast="True" />
          </binding>
        </customBinding>
        
        <netTcpBinding>
          <binding name="RemoteEventsBinding">
            <security mode="None" />
          </binding>
        </netTcpBinding>
 
    </bindings>
    
    ... more stuff ...

</system.serviceModel>


Please note that the <customBinding> element would be used for UDP MC events. It’s safe to leave this in place as a reference.

Configuring "Broadcasters"

The hardest part about configuring "broadcasters" is understanding that "broadcasters" actually act as clients to the listeners. This means that the "broadcasters" call the remote event service on each "listener" server.

To properly configure the "broadcasters", update the <client> element in the <system.serviceModel> element in Web.config. You need to create a separate client endpoint for each enterprise site for each listener server you want to broadcast to. Each client endpoint must have a specific syntax for the name attribute: name="{siteId}-{IP address of server}". Please note that the siteId needs to match the siteId attribute in episerver.config. Also within each collection of client endpoints for a specific target "listener", the endpoints must have unique ports.

So if you have a single "broadcaster" acting as the client to two "listeners" on an EPiServer enterprise environment with two sites you will have four client endpoints.

  • Broadcaster #1 to Listener #1 (Site #1) port 13000
  • Broadcaster #1 to Listener #1 (Site #2) port 13001
  • Broadcaster #1 to Listener #2 (Site #1) port 13000
  • Broadcaster #1 to Listener #2 (Site #2) port 13001

Remember that each client endpoint (within a collection of client endpoints for a specific target "listener" server) will have its own port number. Typically you start at 13000 and work your way up.

The example below should help illustrate.

<system.serviceModel>
  <client>
<!--robstr: no longer using UDP multicast event handling, switched to net.tcp-->
<!--this was previously used for UDP MC event handing
<endpoint
name="RemoteEventServiceClientEndPoint" 
address="soap.udp://224.0.0.1:5000/RemoteEventService" 
binding="customBinding" bindingConfiguration="RemoteEventsBinding" 
contract="EPiServer.Events.ServiceModel.IEventReplication"/>
-->
    <endpoint
        name="MySite-192.168.7.130"
        address="net.tcp://192.168.7.130:13000/RemoteEventService"
        bindingConfiguration="RemoteEventsBinding"
        contract="EPiServer.Events.ServiceModel.IEventReplication"
        binding="netTcpBinding" />
    <endpoint
        name="MySite2-192.168.7.130"
        address="net.tcp://192.168.7.130:13001/RemoteEventService"
        bindingConfiguration="RemoteEventsBinding"
        contract="EPiServer.Events.ServiceModel.IEventReplication"
        binding="netTcpBinding" />
    <endpoint
        name="MySite-192.168.7.131"
        address="net.tcp://192.168.7.131:13000/RemoteEventService"
        bindingConfiguration="RemoteEventsBinding"
        contract="EPiServer.Events.ServiceModel.IEventReplication"
        binding="netTcpBinding" />
    <endpoint
        name="MySite2-192.168.7.131"
        address="net.tcp://192.168.7.131:13001/RemoteEventService"
        bindingConfiguration="RemoteEventsBinding"
        contract="EPiServer.Events.ServiceModel.IEventReplication"
        binding="netTcpBinding" />
  </client>
</system.serviceModel>


Configuring "Listeners"

"Listeners" listen for incoming event broadcasts from the "broadcasters". A listener simply has to have a service endpoint available for the "broadcasters" to connect to. This is done by configuring the appropriate <service> elements in the <system.webService><services> element in Web.config.

In this case you need to make sure to have a separate service available for each EPiServer site (when using EPiServer enterprise). Each service endpoint must have its own port number and the <service> element should have the following pattern syntax for the name attribute: name="{siteId}/EPiServer.Events.Remote.EventReplication".

In the example below, we have two service endpoints (one for each of the two sites). Each "listener" server would have this same configuration (with the exception of the IP addresses).

<system.serviceModel>

... more stuff ...

<!--robstr: we are using net.tcp for load balancing, not UPD mulicast.  The public server configuration must listen, so it has services.-->
  <services>
 
<!-- left over from UDP MC event handling <service name="EPiServer.Events.Remote.EventReplication" xdt:Locator="Match(name)">
<endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" binding="customBinding"
bindingConfiguration="RemoteEventsBinding" address="soap.udp://224.89.89.89:5000/RemoteEventService" xdt:Transform="Insert" />
</service>
-->
 
    <service name="MySite/EPiServer.Events.Remote.EventReplication">
      <endpoint
      name="RemoteEventServiceEndPoint"
      contract="EPiServer.Events.ServiceModel.IEventReplication"
      bindingConfiguration="RemoteEventsBinding"
      address="net.tcp://192.168.7.130:13000/RemoteEventService"
      binding="netTcpBinding" />
    </service>
            
    <service name="MySite2/EPiServer.Events.Remote.EventReplication" >
      <endpoint
      name="RemoteEventServiceEndPoint"
      contract="EPiServer.Events.ServiceModel.IEventReplication"
      bindingConfiguration="RemoteEventsBinding"
      address="net.tcp://192.168.7.130:13001/RemoteEventService"
      binding="netTcpBinding" />
    </service>
  </services>
</system.serviceModel>

That about does it. To test your configuration log onto a "broadcaster" server and do a page change/publish. You should see this change reflected in all the "listener" servers.

Stay tuned for my next blog post of disabling CMS edit/admin mode from the public facing IIS servers.
Thanks!

Robert

Update: One of my colleagues pointed out that the approach below only works if you are sticking to the built in EPiServer caching (page data caching). If you are storing any kind of custom data in the EPiServer cache you will need to account for this in C#.  You'll have to manually flush the cache of your custom data from the EPiServer cache using EPiServerCacheManager.Cache.Remove() when necessary.  This will send out a broadcast event causing all servers that are "listeners" to also flush the custom data in the EPiServer cache.