views:

171

answers:

6

I have a web application that has many faces and so far I've implemented this through creating themes. A theme is a set of html, css and images to be used with the common back end.

Things are laid out like so:

code/
themes/theme1
themes/theme2

And each instance of the web application has a configuration file that states which theme should be used. Example:

theme="theme1"

Now new business rules are asking me to make changes to certain themes that can't be achieved through simply change the html/css/images and require changing the backend. In some cases these changes need to be applied to a group of themes.

I'm wondering how to best lay this out on disk, and also how to handle it in code. I'm sure someone else must have come up against this.

One idea is to have:

code/common
code/theme1
code/theme2
themes/theme1
themes/theme2

Then have my common code set the include_path such that code/theme1 is searched first, then code/common.

Then if I want to specialize say the LogoutPage class for theme2, I can simply copy the page from code/common to the same path under code/theme2 and it will pick up the specialized version.

One problem with this idea is that there'll be multiple classes with the same name. Although in theory they would never be included in the same execution, I wouldn't be able to extend the original base class.

So what if I was to make a unique name for the base class? e.g. Theme1LogoutPage extends LogoutPage. One problem I can foresee with that is when some common code (say the Dispatcher) references LogoutPage. I can add conditions to the dispatcher, but I wonder if there's a more transparent way to handle this?

Another option I can think of is to maintain separate branches for each theme, but I think this could be a lot of work.

One final thing to consider is that features might originate in one theme and then require merging into the common codebase.

Any input greatly appreciated. If it makes any difference, it's a LAMP environment.

+1  A: 

I don't have a specific recommendation. However, I strongly suggest to NOT take shortcut... Use the solution that will you will find comfortable to add a third theme or to change something next year.
Duplication is the enemy of maintainability.

Hapkido
Thanks for the comments Hapkido. I agree with your statement on duplication.
+1  A: 

I'd investigate using the Strategy pattern as a means to implement different functionality in different versions of the site. Have a Factory that takes in your configuration and supplies the appropriate code strategy based on it. Each strategy can implement some common interface so that they are interchangeable from the calling class' point of view. This will isolate your changes to implement new strategies to the Factory class, Configuration class, and any new strategy classes that you need to implement to make the change. You could do the same (or similar) with any user controls that need to differ between the different versions.

I'll illustrate with pseudocode (that may look suspiciously like C#)

public interface ILogoutStrategy
{
   void Logout();
}

public abstract class AbstractLogoutStrategy : ILogoutStrategy
{
   public virtual void Logout()
   {
      // kill the sesssion
   }
}

public class SingleSiteLogoutStrategy : AbstractLogoutStrategy
{
   public void Logout()
   {
      base.Logout();
      // redirect somewhere
   }
}

public class CentralAuthenticationSystemLogoutStrategy : AbstractLogoutStrategy
{
   public void Logout()
   {
      base.Logout();
      // send a logout request to the CAS
      // redirect somewhere
   }
}

public static class StrategyFactory
{
   public ILogoutStrategy GetLogoutStrategy(Configuration config)
   {
      switch (config.Mode)
      {
         case Mode.CAS:
            return new CentralAuthenticationSystemLogoutStrategy();
            break;
         default:
         case Mode.SingleSite:
           return new SingleSiteLogoutStrategy();
           break;

      }
   }
}

Example usage:

ILogoutStrategy logoutStrategy = StrategyFactory.GetLogoutStrategy( config );
logoutStrategy.Logout();
tvanfosson
Thanks for the detailed answer! So what you are suggesting is there should be one codebase. Code shouldn't be specialized based on the intended site, but on its functionality. Then a configuration file (per-site) will state which functionality is offered to the users by that site. Is this right?
A: 

Are you using Master Pages? If you need different layout and UI stuff you could just have a different set of master pages for each of your instances. If you need custom behavior then you might want to look into Dependency Injection. Spring.NET, etc.

sliderhouserules
You've edited your question to indicate your environment is not ASP.NET. I was answering from the assumption you were in ASP.NET. Sorry.
sliderhouserules
A: 

What you need are templates.

Thence you can separate your code from your presentation.

I highly recommend smarty templates. Also PEAR template_it.

http://www.smarty.net/

This also make your code far more maintainable. The aim is to have no html in your php, and to have no php in your html.

then all you will need to do is change the html template that is being used for each theme. or folder of templates.

Bingy
A: 

You could have: /common/code

And: /sitename/code

All files in /common/code are abstract classes. For every file in /common/code, just create a corresponding non-abstract class file in /sitename/code that INHERITS from the abstract class in /common/code.

This way you only need to implement CHANGES in the /sitename/code Everything else is core functionality that exists only in /common/code

The important thing to do here is ensure that you only add public methods to the abstract classes. This way the methods are available to all sites, and classes from all sites can be treated/worked with identically.

lo_fye
A: 

I would do:

[theme name]/[subfolder] default/common default/common/html default/common/css red/code red/common red/common/html red/common/css red/code green/common green/common/html

So if the code or any other component doesn't exist it will fall back to default.

But in fact I would branch the website in svn, so common code if it evolves I can merge it, etc.. see Subversion at: http://subversion.tigris.org/

pre63