Thursday, December 19, 2013

Solving login problems with asp.net framework 4.0 and Internet Explorer 11

So, the joys of working with IE is not only related to layout and javascript, also server functionality can totally stop working, hooray!

 One fun problem is that if you do not have asp.net 4.5 installed on your server, the server will not recognize IE11 as a cookie enabled browser (among other things), making things like session state and forms authentication stop working.

 There are a number of things one can try to resolve this, the most efficient one, but not always available is to simply install .net framework 4.5 on the server, it should be “100%” backwards compatible with 4.0, but somehow I doubt that. Do be aware that you do not have to update your application to compile against .net 4.5, you only need the framework installed on the server.

The second way is to use a custom browser file for IE11, like seen in this SO topic: http://stackoverflow.com/questions/18485339/dopostback-failing-in-ie-11-windows-8-1
This should “solve everything”, including asp.net ajax and other framework functionality that previously did not work in IE11. A problem with this approach is that once you update the server, this browser file will override any potential changes in the machine wide browser files that is added/updated later on (like if you install .net framework 4.5 I can imagine), so this file have to be deleted manually.

The third way is to simply use the old school method of “downgrading” using the IE meta tag, the obvious downside here is that you downgrade any IE11 to run as IE10, but on the other hand, this fix is simple and can be done quite fast:

<meta http-equiv="X-UA-Compatible" content="IE=10" />

And the final way I found, which I used myself on the site I had troubles with (that uses very little asp.net ajax/control features anyways), is to install a patch on the server, and modify web.config. We are not 100% sure the patch is needed, but the blog below seem to suggest that you need it anyways:
The blog: https://gyorgybalassy.wordpress.com/2013/09/23/aspnet-40-forms-authentication-issues-with-ie11/
The patch: http://support.microsoft.com/kb/2836939/en-us
After the patch is installed (or not, you can try without first), you need to update your web.config, and change to cookieless=”UseCookies” in the forms, and sessionstate tags, like for example:
<authentication mode="Forms">
  <forms name=".EPiServerLogin" loginUrl="Logga-in/" timeout="120" defaultUrl="/" cookieless="UseCookies" />
</authentication>


<sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;user id=sa;password=" cookieless="UseCookies" timeout="20" />

This forces all browsers to try and use cookies for session and authentication regardless if they are cookieless or not, which isn't really a big problem cause it wouldn't’t work anyways, so you are not breaking anything that wasn't previously broken.

Thursday, November 21, 2013

Indexing all VPP files with EPiServer 7 and Find

Recently a customer had a problem with a pdf file that didn’t show up on a searchresult page.
Some pdf files was working but others wasn’t. 

The EPiServer Find client configured like this:
FileIndexer.Instance.Conventions.ShouldIndexVPPConvention = new VisibleInFilemanagerVPPIndexingConvention();

Made sure that all vpp folder settings was set to visible in file manager. (showinfilemanager in episerverframework.config).

The problem file was in the Page files folder. So I went to the Find API docs:

And voila, there was the answer, not 100% obvious but here it is, how to index VPP files with Find and Page files as well:
[ModuleDependency(typeof (IndexingModule))]
            [ModuleDependency(typeof(SiteInitializer))]
            public class FindInitialization : IConfigurableModule
            {
                         public void Initialize(InitializationEngine context)
                         {
                                     FileIndexer.Instance.Conventions.ShouldIndexVPPConvention = new VisibleInFilemanagerVPPIndexingConvention();
                             ----> ContentIndexer.Instance.Conventions.EnablePageFilesIndexing();

So we add this line:
ContentIndexer.Instance.Conventions.EnablePageFilesIndexing();
Find docs says this convention should be in the PageIndexer class but now its called ContentIndexer.

Friday, September 27, 2013

House of Sweden - Our new home in Washington D.C.


We are excited to announce our new office space in Washington D.C., located in the House of Sweden. Overlooking the Potomac River, the building is environmentally friendly, focuses on sustainability, and is party friendly - It has an amazing event center.


Come say Hi, and stay for a quick fika.

Nansen Inc.
2900 K Street NW, Apt 502-4
Washington, DC 20007
USA

Friday, August 23, 2013

IE 6/7/8 workaround for custom radio buttons and checkboxes causes page to scroll up

When our client was testing their web site we got feedback regarding a weird scrolling issue that occured when they clicked custom radio buttons and checkboxes. This didn't occur in all browsers, only in Internet Explorer 6, 7 and 8.

The original workaround

The original problem was, and still is for these older IE versions, that when you add custom design to radio buttons and checkboxes you can't use display: none or visibility:hidden to hide the underlying input element. This is because when clicking the label element, IE wont trigger the checked attribute to be set on the input element. So the usual workaround is to still have the input element visible and displayed and instead using something like margin: -10000px or top: -5000px with position: absolute.

The following issue

In your IE 6/7/8 css you have the above mentioned workaround. Everything works fine, you can use the custom checkboxes and radio buttons. 

But wait, there's more.

Since the solution was to use margin: -10000px or top: -5000px - when you click the label at the custom design buttons/boxes IE will focus the browser to the height of the underlying input element that was associated with the label that the user just clicked. 

The result is, if you click the custom button/box when you also have scrolled down a bit, you the browser scrolls back to the top. Because as I mentioned, when the label is clicked, the input field becomes focused.

The final solution

The final solution is nothing special. Just instead of using margin: 10000px or top: -5000px. Use left: -5000px. So the height is as expected, the same height as the original position. The left position just causes it to not be seen by the user. No more weird scrolling when clicking the custom radio button or checkbox, woho!

Wednesday, August 21, 2013

Hidden gems

Some times you just stumble upon a small feature that makes you smile all day. This morning I found one of those.

The pretty print or Javascript formatter buttons. They format the minified script and css files for you so you can debug production sites.
Both IE 10 and Chrome has them. I couldn't find a similar function in Firefox.

In IE you can find it under the script tools button and then selecting Format JavaScript.


 In Chrome the button is more accessible. It's the small { } button at the bottom that silently sits beside the other tools, just waiting for you to click it.

It's a small thing but it sure helped me, I hope it helps make other people smile today :)

Wednesday, August 7, 2013

Shape your destiny through questions

Questions we ask ourselves dictate our mindset, behavior, and energy level. Tony Robbins enlightens us in Awaken the Giant Within, "Questions immediately change what we're focusing on and therefore how we feel. They are the laser of human consciousness. They concentrate our focus and determine what we feel and do."

Up for an experiment? Try these!


The Morning Power Questions 

The following questions are designed to cause you to experience more happiness, excitement, pride, gratitude, joy commitment, and love every day of your life. Remember, quality questions create a quality life.
Come up with two or three answers to all of these questions and feel fully associated. If you have difficulty discovering an answer simply add the word "could." Example: "What could I be most happy about in my life now?
1. What am I happy about in my life now? What about that makes me happy? How does that make me feel?
2. What am I excited about in my life now? What about that makes me excited? How does that make me feel?
3. What am I proud about in my life now? What about that makes me proud? How does that make me feel?
4. What am I grateful about in my life now? What about that makes me grateful? How does that make me feel?
5. What am I enjoying most in my life right now? What about that do I enjoy? How does that make me feel?
6. What am I committed to in my life right now? What about that makes me committed? How does that make me feel?
7. Who do I love? Who loves me? What about that makes me loving? How does that make me feel?

The Evening Power Questions 

1. What have I given today? In what ways have I been a giver today?
2. What did I learn today?
3. How has today added to the quality of my life or how can I use today as an investment in my future?
Repeat the morning questions (optional)

Wednesday, July 3, 2013

List tweets on you site with Linq2Twitter

Now that Twitter API 1 has been closed I found Linq2Twitter and used it in a small project that needed to list tweets from an account with a custom design and it worked great and was easy to setup.

  1. Add a reference to the linq2twitter dll in your project
  2. Create an application at Twitter
  3. Copy the ConsumerKey and ConsumerSecret and inset it into the code example below
  4. Replace the SCREEN_NAME_TO_FILTER_ON with the screen name you want to list tweets from (or do another type of search)
  5. Run the code to see all the tweets
var auth = new ApplicationOnlyAuthorizer
{
  Credentials = new InMemoryCredentials
  {
    ConsumerKey = YOUR_CONSUMER_KEY,
    ConsumerSecret = YOUR_CONSUMER_SECRET
  }
};
auth.Authorize();
var context = new TwitterContext(auth);
var tweets = context.Status.Where(tweet => tweet.ScreenName == SCREEN_NAME_TO_FILTER_ON && tweet.Type == StatusType.User).Take(4);
The tweet text is just plain text with no links for hash tags or other stuff but you can use this code to format the tweet,
protected string FormatTweet(Status status)
{
  var entities = new List<EntityBase>();
  entities.AddRange(status.Entities.HashTagEntities);
  entities.AddRange(status.Entities.UrlEntities);
  entities.AddRange(status.Entities.UserMentionEntities);
  entities = entities.OrderByDescending(item => item.Start).ToList();
  var linkedText = status.Text;
  foreach (var entity in entities)
  {
    if (entity is HashTagEntity)
    {
      var tagEntity = (HashTagEntity)entity;
      linkedText = string.Format(
          "{0}<a href=\"http://twitter.com/search?q=%23{1}\">{1}</a>{2}",
          linkedText.Substring(0, entity.Start),
          tagEntity.Tag,
          linkedText.Substring(entity.End));
    }
    else if (entity is UserMentionEntity)
    {
      var mentionEntity = (UserMentionEntity)entity;

      linkedText = string.Format(
          "{0}<a href=\"http://twitter.com/{1}\">@{1}</a>{2}",
          linkedText.Substring(0, entity.Start),
          mentionEntity.ScreenName,
          linkedText.Substring(entity.End));
    }
    else if (entity is UrlEntity)
    {
      var urlEntity = (UrlEntity)entity;
      linkedText = string.Format(
          "{0}<a href=\"{1}\">{1}</a>{2}",
          linkedText.Substring(0, entity.Start),
          urlEntity.Url,
          linkedText.Substring(entity.End));
    }
  }
  return linkedText;
}

Monday, July 1, 2013

Sunshine with cloud focus at Build

My week at Build here in San Francisco is at an end, it’s been a blast. The initial day of the conference had more of a device and OS focus going through a lot of what’s new in Windows 8.1 and the devices it will run on. The other half focused more on the Azure platform together with new features in Visual Studio. On a side note I’m writing this on a Surface Pro that they were nice enough to hand out to all attendees. It’s a cool piece of hardware, and basically a laptop with the tablet form factor. It’s powerful enough to run Visual studio which is great, but don’t have enough “oomph” to replace my dev laptop. If you already have a somewhat portable laptop and a tablet the Surface Pro is probably an unnecessary addition to your gadget collection. In theory it  could replace both but the design makes it more a tablet than a laptop. I’m looking forward trying it out as an “on the go” alternative to my laptop, and with the included stylus – using one note to take notes is a breeze.

Session wise my second part of build was mainly Azure and VS 2013 focused. The line to Hanselman’s ASP.NET session rivaled the goodie bag line and I ended up watching that session online. The developer of Web Essentials, Mads Kristensen showed off some of the new features in VS 2013 and Web Essentials available for html, css & javascript editing. The new browser link functionality they have as a proof of concept in 2013 seems like an awesome tool, it’s also easy to extend which means we’ll most likely see a lot of great plugins created by the dev community for this one. It’s utilizing signalR to send updates in real time to the browser and keeping them synced with the changes made in visual studio. Really looking forward to using this in future development.

Other sessions showed us how to combine the ease of setting up sites in azure with build automation, test scripts and even on-premise build server such as TeamCity. Interesting stuff!

Thursday night ended in the judging of the Hackaton competition and the attendee party. Impressive what the devs in the Hackaton managed to put together with just in just a couple of days. The party was held down at pier 48 stacked with pool tables, pinball machines, air hockey tables and classic arcades. Add some live music to that and I’d say they manage to create the perfect environment for the 6000 geeks at build to hang out Smile

All in all I think it was a great event, well organized, great sessions and an overall feel that the MS dev community is exited over what’s coming. Before leaving San Francisco I managed to catch the Pride Festival going on this weekend, people everywhere and a parade with about 199 different groups spanning for about 4 hours. Great way to end an exiting week here in San Francisco.

Here are links to some of the sessions I found really interesting. I really recommend watching them when you have some time to kill Smile

 

Thursday, June 27, 2013

A day at Microsoft Build 2013

This year’s build sport a whooping 6000 attendees and takes place at the Moscone Center in San Francisco.
WP_20130625_023
Tuesday’s registration line.

Keynote

- Touch, touch, touch
- Rapid release, rapid release, rapid release
As you might suspect, the source of these sentences was Microsoft’s CEO Steve Ballmer. Enthusiastic as ever he opened up this year’s Build conference focusing on the wide range of available devices all running windows, and the upcoming 8.1 release. As well as demo’s of some of the new features in Windows 8.1, Microsoft showcased the Preview release of Visual Studio 2013, new API’s for the Bing platform and Project Spark.

WP_20130626_002
For coverage of the event channel9 got you covered

I found the 1st day’s keynote interesting, and it feels like as a developer on the Microsoft platform, there’s a lot of good things up ahead.

WP_20130626_016
Demo of the upcoming 3D printer support in windows 8.1

Sessions

There’s a lot to pick from on the agenda, and I suspect the most crowded sessions today were the one’s focusing on the new features in 8.1 for app development. My day ended up having more of a Visual Studio & Azure focus, finishing of with a real mind bender session on developing Neural Networks.
Really looking forward to trying out some of the new features in VS 2013 – Easier code “navigation” with inline preview’s, profile specific settings connected to live account, easier async debugging and more. Another interesting session showed us how to utilize the existing load testing capabilities of Visual Studio Ultimate, and now with VS 2013 you can run your test agents in the cloud via Team Foundation Services. Seems like a great option when a large amount of test agents are needed temporarily.

WP_20130626_019
Load testing in the cloud.

As I mentioned, the last session of the day was a bit of mind bender. The title was “Developing Neural Networks Using Visual Studio” – I thought placing it in the 5pm – 6pm slot was a bit optimistic, but the speaker James McCaffrey was easily one of the best I’ve listened to. A very engaging talk on a super complex subject. I had no experience what so ever on the subject but it sure made me want to look into it further though.
All in all an awesome day here in San Francisco, tomorrows keynote will most likely focus heavily on Azure and it’s related services – Looking forward to it Smile

Saturday, June 8, 2013

North Kingdom @ OFFF Barcelona 2013

Yesterday we were fortunate to see North Kingdom's session where they talked about their collaboration with Adidas. During the last 2 years NK has worked together with Adidas to create a digital archive containing all their products over the years, products associated with memorable sport events. A goldmine for nostalgics and sports fans.


http://vimeo.com/61886081
https://www.adidas-archive.org/

Thursday, May 30, 2013

Custom Checkbox and Radio Buttons with Icon Fonts

There are lots of tutorials on how you create your own custom radio buttons and checkboxes with pure CSS. But I haven't seen one that combines the :checked technique AND the use of icon fonts so I thought I should write a little something something on the subject. I'll assume that you already know how much more awesome an icon font is compared to an image or a sprite. A couple of reasons: they scale, they're Retina Ready and it just take a second to change their color or size. It's a long list, trust me!

Convinced? Good! Now the first thing you need to do is create your own custom font. Over at icomoon.io they have a really nice app for this. When you have generated your font, grab the font-face rule that came together with your icon font.

Then in your CSS file you set inputs to display:none, they're ugly as hell remember? Then we'll use the pseudo-element :before on our labels like so:

input[type='checkbox'] + label:before,
input[type='radio'] + label:before {
font-family: 'icomoon';
}

input[type="radio"] + label:before {
  content: "\e003"; /* Radio Button Unchecked */
}

input[type="radio"]:checked + label:before {
  content: "\e002"; /* Radio Button Checked */
}

input[type="checkbox"] + label:before {
  content: "\e000"; /* Checkbox Unchecked */
}

input[type="checkbox"]:checked + label:before {
  content: "\e001"; /* Checkbox Checked */
}

Then in the mark-up you write standard html, no rabbit-in-a-hat-tricks!

And there you go! Check out a demo here. This works from IE8 and up, if you need deeper browser support I'd check out Selectivizr. Thank you for reading, hope I helped!


Friday, May 17, 2013

Internet Explorer version ≤8 parseInt() octal radix default

Old browsers, like IE7 and IE8 ( that uses ECMAScript older than 5) uses octal radix ( or base ) as default when parsing strings that start with “0” as ints. Internet Explorer 8 and older versions therefore have this rather peculiar default behavior.

parseInt(“01”) => 1*8^0 = 1
parseInt(“05”) => 5*8^0 = 5
parseInt("055") => (5*8^1)+(5*8^0), 40+5 = 45
parseInt(“07”) => 7*8^0 = 7

parseInt(“08”) => 0*8^0 = 0 (the 8 has no equivalent in base 8)
parseInt(“09”) => 0, same issue

parseInt(“10”) => (0*10^0) + (1*10^1) = 10 ( IE8 recognizes this as base 10, as it doesn’t start with zero.) 

Say that you're parsing months as ints, 01-12. 01-09 will be parsed as base 8, and 10-12 as base 10.
It'll work in all new browsers, but 08 and 09 will be parsed as zero in IE8 or older ( or other old browsers ).

Luckily, for us all, there is a way to specify which base to use.

parseInt(string,base);
parseInt(“08”,10) => 8*10^0 = 8

Friday, May 3, 2013

Marketing Automation – Shifting the way we engage individuals.

Insights from Silverpop’s Digital Marketing University

Silverpop’s Digital Marketing University (#DMU13) in Chicago had a packed house, over 200 people poised with pen, pad, and open mind ready to absorb marketing automation knowledge from the experts. Silverpop’s Director of Product Strategy Brian Brown (@GetVision) began the conference with an explanation of what we’re witnessing today, the “channel explosion.” Blogs, Facebook, Twitter, and Pinterest, the list of channels that marketers are asked to leverage continually grows. The more channels, the more work for the marketing team, but not necessary more reward! The channel explosion has changed how we communicate, learn, and buy, explained Brown. Consumers research product information online, and visit retail outlets when they’re ready to purchase. They know the World Wide Web often supports more informed decision-making than sales associates.

Individuals today are required to aggressively filter overwhelming amounts of content a result of the fusion between mass marketing and the channel explosion. How we’re engaging with them online has to change. To reach our customers we must move away from mass marketing, away from segmented marketing, and strive for individual marketing. Think, “share-worthy.” Our focus should change from generating revenue to creating experiences (that generate revenue), suggests Brown. Creating experiences for consumers means timely delivery of content that is uniquely relevant.

In the world of automated marketing we aren’t blasting emails to lists. Information about our customers’ behavior drives our rules for engagement. Listen… “Set it and forget it,” recommended Silverpop’s Product Evangelist Dave Walters. The information utilized to create rules can be anything from pages visited, questions answered, whitepapers downloaded, emails opened, read, or responded to, forms filled out, or any combination of the above. Content delivered can be equally dynamic: emails, sets of emails, web page content, notifications to sales teams, progressive forms, or anything else digital you can imagine. Automation is shifting the marketer’s job to a higher level.

If we view the benefits of marketing automation through a lens of the time management matrix created by Stephen Covey, author of The 7 Habits of Highly Effective People we see that we are able to spend more time on quadrant 2, important and not urgent, activities. In his book Covey emphasizes that quadrant 2 activities --capability improvement, relationship building, recognizing new opportunities, and planning -- provide the most return per time invested. Isn’t that what we’re seeking?

The time management matrix

Marketing teams in the future will spend more of their with whiteboard discussions to identify the best ways to engage individuals. Content creation is still central to the marketer’s strategy, but rather than being delivered at once, content will live forever (or as long as relevant) in the automation system, and delivered to individuals at appropriate times based on defined rules. Over time our base of content and rules for engagement will grow, we will focus our energy on refining our message rather than pressing the send button, and we will witness huge increases in our conversion rates!

If you happen to see one of Silverpop’s Digital Marketing Universities near you I recommend attending! Thanks to everyone at the Silverpop team for putting on a great event.

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.

Wednesday, March 20, 2013

Set up an EPiServer site in a snap

Do you also work at a digital agency where you have several dozen of customer sites? In that case I bet you have spent some time setting up sites in IIS web server, followed by trying to remember the path to the HOSTS file and finding an appropriate EPiServer license file that you then copy to the site.

Sure, it does not take that long to do it all but it sure as heck is boring and repetitive. It can easily be avoided by some automation.

WIA is the new kid on the block

To automate this process I decided to start a project called WIA, that is an easy to use command line tool. It will do all the steps required to set up an EPiServer CMS site, all before you have had a chance to take another sip of your coffee.



Actually, WIA will do all the following steps for you:

- Figure out what kind of project you have (EPiServer and .NET versions etc).
- Create a new site in IIS with appropriate configuration.
- Add a site mapping in EPiServerFramework.config matching your new IIS site.
- Update the HOSTS file with an entry for the web project's URL.
- Copy a license file for EPiServer CMS to the web directory.
- Build the solution and display any errors that occurred.
- Ping the site so that it starts up.

Sound good. Now let me try it

Does this tool sound like something that would improve your workflow? Head over to the git-repo at https://github.com/nansen/wia for instructions how to install the tool and its usage.

If you find problems please feel free to submit issues at the Github page, or even a pull-request if you are feeling kind.

Friday, March 15, 2013

Hide tabs in EPiServers edit mode if not in certain role



Recently I had a customer requirement where a certain role should only be able to edit one specific property on the page.
I couldn't find any built in support in EPiServer for this, so I had to figure out another way to solve this problem.

Inspired by this blog post by David Knipe I decided to go for an Edit plugin with the following code:


 [GuiPlugIn(Area = PlugInArea.EditPanel)]
 class HideTabsFromRole : ICustomPlugInLoader
 {
  public PlugInDescriptor[] List()
  {
   EditPanel.LoadedPage += EditPanel_LoadedPage;

   return new PlugInDescriptor[0] { };
  }

  void EditPanel_LoadedPage(EditPanel sender, LoadedPageEventArgs e)
  {
   if (!HttpContext.Current.User.IsInRole("MyRole")) return;

   foreach (var propertyData in e.Page.Property.Where(propertyData => propertyData.Name != "MyProperty"))
   {
    e.Page.Property[propertyData.Name].DisplayEditUI = false;
   }
  }
 }


Basically what I'm doing is first checking if the user is in the role and if so then hide the properties, except for the property I want to display for this role. If there's no visible properties left on the tab the actual tab will not be rendered. The last part is default functionality in episerver.

Maybe this solution by Mark Everard would be a neater way of solving this problem, but this time I chose to take a shortcut.


Thursday, March 14, 2013

T-commerce larger than m-commerce for the first time!


Since January 2013 are different tablets (iPad, Android) responsible for more traffic than smartphones. All according to a new Adobe document. More than 100 bil­lion vis­its to 1000+ web­sites world-wide was evaluated. 

Consumers prefer the tablet when spending time shopping. Dur­ing the past hol­i­day shop­ping sea­son 13,5 % of all online sales were trans­acted via tablets. 



13926_di_tablet_smartphone_growth

13926_di_global_traffic_device