Pages

Thursday, October 22, 2009

More EpiServer extension methods

Here's another extension method for returning a normal EpiServer Url to a page from a LinkItem (from a PropertyLinkCollection).

public static class LinkItemExtensions
{
public static string ToExternal(this LinkItem item)
{
string externalUrl = item.Href;

PermanentLinkMapStore.TryToMapped(item.Href, out externalUrl);

return externalUrl;
}
}


I'm using the PermanentLinkMapStore following a read of this post.

I've been using it when binding a LinkItemCollection to a ListView like this:
PropertyLinkCollection linksProperty = CurrentPage.ToProperty("Links");

var links = from l in linksProperty.Links
select new
{
Href = l.ToExternal(),
Title = l.Title,
Text = l.Text,
Extension = Path.GetExtension(l.Href)
};

lvLinks.DataSource = links;
lvLinks.DataBind();

Wednesday, October 14, 2009

EpiServer Extension methods

Following on from my previous post I realised that the majority of the Extension methods that I use in everyday coding are EpiServer related.

Here are some really useful ones:


/// <summary>
/// Gets the children.
/// </summary>
/// <param name="page">The page.</param>
/// <returns></returns>
public static PageDataCollection GetChildren(this PageData page)
{
PageDataCollection children = new PageDataCollection();

if (page.PageLink != null && page.PageLink.ID > 0)
{
children = DataFactory.Instance.GetChildren(page.PageLink);

FilterPublished publishedDateFilter = new FilterPublished(PagePublishedStatus.Published);
FilterSort indexSorter = new EPiServer.Filters.FilterSort(FilterSortOrder.Index);

publishedDateFilter.Filter(children);
indexSorter.Sort(children);
}

return children;
}

/// <summary>
/// Gets the external URL.
/// </summary>
/// <param name="page">The page.</param>
/// <returns></returns>
public static string GetExternalUrl(this PageData page)
{
string result = String.Empty;

if (page != null)
{
UrlBuilder builder = new UrlBuilder(page.LinkURL);

EPiServer.Global.UrlRewriteProvider.ConvertToExternal(builder, page, Encoding.UTF8);

result = builder.ToString();
}

return result;
}


/// <summary>
/// Returns a concrete PropertyData (or child class)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="page">The page.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public static T ToProperty<T>(this PageData page, string propertyName) where T : PropertyData
{
return page.Property[propertyName] as T;
}

/// <summary>
/// Returns a PageData property or string.Empty if null
/// </summary>
/// <param name="page">The page.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public static string SafeProperty(this PageData page, string propertyName)
{
return (page.HasProperty(propertyName)) ? page[propertyName].ToString() : string.Empty;
}


/// <summary>
/// Gets the page for a Pagereference.
/// </summary>
/// <param name="pageRef">The Pagereference.</param>
/// <returns></returns>
public static PageData GetPage(this PageReference pageRef)
{
return DataFactory.Instance.GetPage(pageRef);
}

Some useful Extensions methods

Extension methods have been around a while now and I like them more every day. Oh I know they are just aliased static methods, but they are convenient for small common situations where it seems like the framework developers missed something out.

Here's a few of my that I find useful:

NameValueCollection Extensions:

/// <summary>
/// Returns the collectionas a standard URL type QueryString
/// </summary>
/// <param name="self">The self.</param>
/// <returns></returns>
public static string ToQueryString(this NameValueCollection self)
{
string qsString = string.Empty;
if (self.Count > 0)
{
StringBuilder qsBuilder = new StringBuilder("?");

for (int i = 0; i < self.Keys.Count; i++)
{
if (self.Keys[i] != null && self.Keys[i].Length > 0)
{
qsBuilder.AppendFormat("{0}={1}&", HttpContext.Current.Server.UrlEncode(self.Keys[i]), HttpContext.Current.Server.UrlEncode(self[i]));
}
}

qsString = qsBuilder.ToString();
}

return qsString;
}


/// <summary>
/// Creates a copy the specified NameValueCollection.
/// </summary>
/// <param name="self">The NameValueCollection.</param>
/// <returns></returns>
public static NameValueCollection Copy(this NameValueCollection self)
{
return new NameValueCollection(self);
}


String Extensions

/// <summary>
/// Capitalizes the specified word.
/// </summary>
/// <param name="word">The word.</param>
/// <returns></returns>
public static string Capitalize(this string word)
{
if (word.IsNullOrEmpty())
{
return word;
}

// The aggregate is because IEnumerable.ToString doesn't return the characters as a string, it returns the type's name as a string.
return word[0].ToString().ToUpper() + word.Skip(1).Aggregate("", (s, c) => s + c);
}


/// <summary>
/// Splits the specified string into a list of white-space separated word strings.
/// </summary>
/// <param name="s">The s.</param>
/// <returns></returns>
public static IEnumerable Wordify(this string s)
{
return s.Split(new char[]{' ', '\n', '\t', '\r', '.', ',', ';', ':', '-'}, StringSplitOptions.RemoveEmptyEntries);
}

Wednesday, September 16, 2009

Custom styles in EPiServer WYSIWYG

If you've been using EPiServer for a while you'll know the frustration of the built in WYSIWYG editor for rich text entry. It's IE only and rather clunky.



There is a way to add your own custom HTML and CSS classes into the editor which get added to a drop down box.



By selecting an item from this drop down you can insert both HTML tags and classes into the text.

To do this you need to set the 'Path to CSS file for Editor' either in the Web.Config



<sitesettings uieditorcsspaths="/styles/episerver.css" />


In the CSS file the secret is a custom style attribute called EditMenuName which gives the name to put in the drop down list in the editor:

/*
Inserts <h1> </h1>
*/
h1
{
   EditMenuName:Heading1;
}

/*
Inserts <h2> </h2>
*/
h2
{
   EditMenuName:Heading2;
}

/*
Inserts <p class="intro"> </p>
*/
p.intro
{
   EditMenuName:IntroParagraph;
   padding-bottom: 1em;
   border-bottom: 1px solid #E8E8E8;
}

/*
Inserts <div class="box white"> </div>
*/
div.box white
{
   EditMenuName:Rounded Corner Box;
   border: solid 1px #E8E8E8;
   padding: 10px;
}

Thursday, June 18, 2009

Simple UrlRewriter for EpiServer

On an EpiServer project that I just finished there was a requirement that all URLs generated by the CMS should be lowercase. I couldn't really see the issue. I know that web servers are allowed to be case sensitive with URLs, but didn't really see the big deal.

However, looking at EpiServer, I thught that I might re-write a PageData extension method which I wrote previously which generates the external URL of a page:

public static string GetExternalUrl(this PageData page)
{
string result = String.Empty;

if (page != null)
{
UrlBuilder builder = new UrlBuilder(page.LinkURL);
EPiServer.Global.UrlRewriteProvider.ConvertToExternal
(builder, page, Encoding.UTF8);

result = builder.ToString();
}

return result;
}

This might have worked, but was still a bit of a kludge, so I turned to the undocumented waters of the EPiServer.Web.FriendlyUrlRewriteProvider

The FriendlyUrlRewriteProvider has a virtual method called:

ConvertToExternalInternal(EPiServer.UrlBuilder url, object internalObject, Encoding toEncoding)

Which is called by the EPiServer.Global.UrlRewriteProvider.ConvertToExternal(...) method I use in the Extension method above. Overriding that mehtod and converting the UrlBuilder's path property to lowercase seemed to do the trick.

Here's the final class:

public class LowerCaseUrlRewriteProvider : EPiServer.Web.FriendlyUrlRewriteProvider
{
public LowerCaseUrlRewriteProvider()
: base()
{

}

protected override bool ConvertToExternalInternal
(EPiServer.UrlBuilder url, object internalObject, Encoding toEncoding)
{
base.ConvertToExternalInternal(url, internalObject, toEncoding);

url.Path = url.Path.ToLower();

return true;
}
}



Update: Setting it up in the Web.Config

You now need to add this to the urlRewrite EpiServer config element in the Web.Config:

<urlRewrite defaultProvider="LowerCaseUrlRewriteProvider">
<providers>
<add name="LowerCaseUrlRewriteProvider" type="Shared.EpiServer.Web.LowerCaseUrlRewriteProvider,Shared.EpiServer" />
<add name="EPiServerFriendlyUrlRewriteProvider" type="EPiServer.Web.FriendlyUrlRewriteProvider,EPiServer" />
<add description="EPiServer identity URL rewriter" name="EPiServerIdentityUrlRewriteProvider"
type="EPiServer.Web.IdentityUrlRewriteProvider,EPiServer" />
<add description="EPiServer bypass URL rewriter" name="EPiServerNullUrlRewriteProvider"
type="EPiServer.Web.NullUrlRewriteProvider,EPiServer" />
</providers>
</urlRewrite>

Wednesday, April 29, 2009

MVC Controller for webapp using Routes

Foloowing on from my previous post my MVC home brew is coming along slowly. I decided to implement Routes (Rails style routing for Python), basing my wsgi class on one I found on the App Engine Recipe site.

I altered the wsgi class very slightly to use a constructor passing in the request and response objects.
# Initialize matched controller from given module.
__import__(module_name)
module = sys.modules[module_name]

if (module is not None and hasattr(module, class_name)):
controller = getattr(module, class_name)(request, response)
else:
raise ImportError('Controller %s could not be initialized.' % (class_name))

The Controller itself acts as a base class for the actual concrete controllers which will be used in the application itself.
import os
import urlparse
import cgi
import logging
import sys
import traceback
from google.appengine.ext.webapp import template
from SessionManager import SessionManager
from routes.util import url_for

class Controller(object):
def __init__(self, request, response):
self.request = request
self.response = response
self.sessionManager = SessionManager(self.request, self.response)
self.session = self.sessionManager.current()
self.initialize()

def initialize(self):
pass

def handle_exception(self, e, debug):
#self.error(500)
logging.exception(e)
if debug:
lines = ''.join(traceback.format_exception(*sys.exc_info()))
self.response.clear()
self.response.out.write('%s' % (cgi.escape(lines, quote=True)))

def error(self, code):
"""Clears the response output stream and sets the given HTTP error code.

Args:
code: the HTTP status error code (e.g., 501)
"""
self.response.set_status(code)
self.response.clear()

def rendertemplate(self, context, path):
if (path is None):
path = __file__ + '.html'

if (context is None):
context = dict()

context['session'] = self.session
context['request'] = self.request

path = os.path.join(os.path.dirname(__file__), path)
logging.warn(path)
self.response.out.write(template.render(path, context))

def redirect(self, uri, permanent=False):
"""Issues an HTTP redirect to the given relative URL.

Args:
uri: a relative or absolute URI (e.g., '../flowers.html')
permanent: if true, we use a 301 redirect instead of a 302 redirect
"""
if permanent:
self.response.set_status(301)
else:
self.response.set_status(302)

absolute_url = urlparse.urljoin(self.request.uri, uri)
self.response.headers['Location'] = str(absolute_url)
self.response.clear()

The Controller class has a couple of methods of note:

def rendertemplate(self, context, path): which takes a dictionary (context) and the relative path to an HTML template. The templates themselves will use the Django template markup tags.

def redirect(self, uri, permanent=False): which does an HTTP 301 or 302 redirect.

Here's an example concrete Controller class where the view method is the action as defined using Routes.

import twitter as twitter
from controllers.Controller import Controller as controllerBase

class ConcreteController(controllerBase):
def view(self, id):

data = doSomething(id)

template_values['data'] = data

self.rendertemplate(template_values, '../views/template.html')

Saturday, April 25, 2009

Session handling with Memcache API in Google AppEngine

I've been trying to teach myself Python by witting a basic Twitter client in Google AppEngine. 

One of the things which I found lacking from the Webapp and Django frameworks was anything to manage sessions well. So, realising that the Memcache API has a timeout I figured that I could use that as the basis for session management. 

I ended up creating 2 classes, a SessionManager class to create, retrieve and delete sessions ans a Session object which would act as a simple dictionary to store arbitrary session values in. 

I would then be able to use the Session and SessionManager classes like this in my Controller class: 
class Controller(object):
def __init__(self, request, response):
self.request = request
self.response = response
self.sessionManager = SessionManager(self.request, self.response)
self.session = self.sessionManager.current()
self.initialize()


The SessionManager constructor needs both Request and Response objects because it needs to read and write Cookies. 

Here's the SessionManager class: 
from google.appengine.api import memcache
import datetime
import random

class SessionManager(object):
def __init__(self, request, response, timeout=1200):
self.timeout = timeout
self.request = request
self.response = response
self.cookieName = 'SID'
def current(self):
cookievalue = self.request.cookies.get(self.cookieName)
if ((cookievalue is not None) and (memcache.get(cookievalue) is not None)):
return Session(cookievalue, self.timeout, False)
else:
return self.createSession()
def createSession(self):
newId = self.createNewId()
memcache.set(key=newId, value=True, time=self.timeout, )
now = datetime.datetime.now()
inc = datetime.timedelta(seconds=self.timeout)
now += inc
self.setCookie(key=self.cookieName,value=newId,expires=now)
return Session(newId, self.timeout, True)

def destroySession(self):
self.clearCookie(self.cookieName)
def createNewId(self):
newHash = str(hash(datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S%f'))) + str(random.random())
while memcache.get(newHash) is not None:
newHash = self.CreateNewId()
return newHash
def setCookie(self,key,value,expires,path='/'):
self.response.headers.add_header('Set-Cookie', key+'='+value+ ' path='+path+'; expires '+expires.strftime('%a, %d-%b-%Y %H:%M:00 %Z'))
def clearCookie(self,key):
self.setCookie(key=key,value='',expires=datetime.datetime.now())


I'm creating a unique sessionId by concatenating the current date/time with a random number. 

Here's the Session class: 
class Session(object):
def __init__(self, id, timeout, isNew=False):
self.id = id
self.IsNew = isNew
self.keys = dict()
self.timeout = timeout
memcache.set(key=self.id+'__keys', value=self.keys, time=timeout)
def __getitem__(self, key):
return memcache.get(self.id+'_'+key)
def __setitem__(self,key,value):
memcache.set(key=self.id+'_'+key, value=value, time=self.timeout)
self.keys[key] = value
memcache.set(key=self.id+'__keys', value=self.keys, time=self.timeout)
def hasKey(self, key):
self.keys = memcache.get(self.id+'__keys')
return (key in self.keys)


That's kind of it really. It's working well at the moment but I've not used it with very large numbers of concurrent users.

Sunday, February 22, 2009

Virtual site handler in .net

I cam across a situation the other day where I found that my ASP.NET was limited to 1 website and I wanted to be able to host more than one website. Each site wasn't going to be that big or clever so I decided to see if I could come up with an IHttpModule which would be able to make it serve different pages depending on which domain was requested.

The core of the solution I came up with is the VirtualSiteManager class which given a request will return a re-written path which an IHttpModule can rewrite to.
public class VirtualSiteManager
{
public VirtualSiteManager(string defaultSiteName)
{
_defaultSiteName = defaultSiteName;
Sites = new Dictionary<string, VirtualSite>();
}

private string _defaultSiteName;

private static VirtualSiteManager _instance;
private static Type _lock = typeof(VirtualSiteManager);

public static VirtualSiteManager Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = (VirtualSiteManager) WebConfigurationManager.GetSection("virtualSite");
}
}

return _instance;
}
}

public VirtualSite DefaultSite
{
get
{
VirtualSite defaultSite = null;

if (Sites.ContainsKey(_defaultSiteName))
{
defaultSite = Sites[_defaultSiteName];
}

return defaultSite;
}
}

public Dictionary<string, VirtualSite> Sites
{
get;
private set;
}

public void AddSite(VirtualSite site)
{
if (!Sites.ContainsKey(site.Host))
{
Sites.Add(site.Host, site);
}
else
{
Sites[site.Host] = site;
}
}

public void AddSite(string name, string host, string virtualPath)
{
AddSite(new VirtualSite(name, host, virtualPath));
}

public string RewritePath(HttpContext context)
{
string host = context.Request.Url.Host;
VirtualSite handlingSite = (Sites.ContainsKey(host)) ? Sites[host]: DefaultSite;
string currentPath = context.Request.CurrentExecutionFilePath;

context.Response.AddHeader("X-Debug-currentPath", currentPath);

string returnPath = handlingSite.VirtualPath + (currentPath.StartsWith("/") ? currentPath.Substring(1) : currentPath);

if (returnPath.EndsWith("/"))
{
returnPath += "index.aspx";
}

return returnPath;
}
}

I store the domains and what their virtual path's are in the Web.Config like this:
<configSections>
<section name="virtualSite" type="RomfordEvan.Web.Components.VirtualSite.VirtualSiteHandler, RomfordEvan.Web.Components"/>
</configSections>
<virtualSite default="default">
<site name="default" host="www.domain1.com" virtualPath="/"/>
<site name="localhost" host="localhost" virtualPath="/"/>
<site name="domain2" host="www.domain2.com" virtualPath="/domain2/"/>
</virtualSite>

The IConfigurationSectionHandler to read the Web.Config and create the VirtualSiteManager looks like this:
public class VirtualSiteHandler : IConfigurationSectionHandler
{
#region IConfigurationSectionHandler Members

public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
XmlAttribute def = section.Attributes["default"];

string defaultSiteName = string.Empty;

if (def != null && def.InnerText.Length > 0)
{
defaultSiteName = def.InnerText;
}

VirtualSiteManager manager = new VirtualSiteManager(defaultSiteName);

XmlNodeList siteNodes = section.SelectNodes(".//site");

foreach (XmlNode siteNode in siteNodes)
{
manager.AddSite(siteNode.Attributes["name"].InnerText, siteNode.Attributes["host"].InnerText, siteNode.Attributes["virtualPath"].InnerText);
}

return manager;
}

#endregion
}

This works fine with the following caveats:
  • Under IIS6 it requires wildcard mapping to the ASP.NET ISAPI filter.
  • You will need to add HttpHandlers for all the file types which your site will serve (see below)

<add verb="GET,POST" path="*/" validate="true" type="System.Web.UI.PageHandlerFactory" />
<add verb="GET" path="*.html" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.js" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.gif" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.jpg" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.png" validate="true" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.css" validate="true" type="System.Web.StaticFileHandler" />

Wednesday, February 18, 2009

Generic, type safe Configuration Manager

I like to store lots of config data in the Web.Config and I get fed up with having to cast the ConfigSection to the concrete class:
MyConfigSectionHandler handler =
(MyConfigSectionHandler)
WebConfigurationManager.GetSection(sectionName);

So I've written a helper class where now you can use:
MyConfigSectionHandler config =
ConfigManager.Instance.GetSection<MyConfigSectionHandler>
(sectionName);

Here's the code - (I've implemented it as a Singleton for a reason I don't remember now):
public class ConfigManager
{
private static ConfigManager _instance;
private static object _lock = typeof(ConfigManager);

private ConfigManager()
{

}

/// <summary>
/// Gets a singleton instance.
/// </summary>
/// <value>The instance.</value>
public static ConfigManager Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ConfigManager();
}

return _instance;
}
}
}

/// <summary>
/// Gets the section.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sectionName">Name of the section.</param>
/// <returns></returns>
public T GetSection<T>(string sectionName)
where T : class
{
return WebConfigurationManager.GetSection(sectionName) as T;
}
}