A Multi-Site Approach in Concrete5

After seeing that many were having trouble understanding how they could use a single core Concrete5 installation to run multiple sites, I thought I would write this post to provide a strawman to help facilitate the discussion of how this can be done.

I run a server in the cloud using Amazon’s EC2 service and utilise an Ubuntu server with the usual LAMP setup.

1. Core Installation Preparation

To start, I have my core Concrete5 installation files in my default web site folder:

/var/www/default/concrete5.4.0.5

I keep the version number as this allows for the many different sites to use different cores if you wish.  How this is achieved will become clear later on in the post.

This basic setup of copying files is covered on the concrete5 site (http://bag.gs/hEWOT9) so I’m not going to repeat that here but it is important to say just copy the files for now.

N.B. Don’t go to the installed location in a browser yet – just copy the files.

2. Set up the domain that you want to host

This means that you can set up a separate virtual host in Apache or however which way you’d like to.  A search (http://bag.gs/heaC2L) will surely find you some assistance here.

The end result will be a virtual host that corresponds to your domain (e.g. www.exampledomain.com) and a separate directory from which that site is served on your server.  In my case, this directory is:

/var/www/example.com

N.B. I’m assuming that you’ve purchased your domain and have all the right DNS entries set to point to your server.

3. Set up site specific structure

This step is reasonably simple.  We are going to copy the entire contents of the core installation folder (concrete5.4.0.5 in my case) over to the new site directory so that we have the following:

/var/www/example.com/blocks
/var/www/example.com/concrete
/var/www/example.com/config
...
/var/www/example.com/updates

This copies what are in the most part empty directories and the inner concrete.  The root index.php is important as well as the couple of files in the config directory so make sure you take those across.

As concrete5 is well designed, the guys who have created the product have nicely permitted the extension of almost everything through utilising the directory hierarchy.  This means that as site developers, we should never touch the content of the internal concrete directory.  In the above case, the /var/www/example.com/concrete directory.  Consider it sacred!

If we obey this principle of not messing with anything in the core concrete folder, which helps us all upgrade, then you would notice that there is no value in that the …/example.com/concrete folder being a copy of the …/default/concrete5.4.0.5/concrete folder and you’d be correct.

Therefore, we are going to delete the /var/www/example.com/concrete directory and replace it with a symbolic link.  It is the symbolic link that allows us to point to different cores for different sites.

The syntax for the symbolic link when in the /var/www/example.com directory is:

...:/var/www/example.com$ ln -s ../default/concrete5.4.0.5/concrete concrete

Your Done!

Seriously, that’s all.  You can then pick up the basic setup (http://bag.gs/hEWOT9) at the point where you navigate to the new site, enter the site name, URL, and Database settings and away you go.

This small amount of preparation allows you to scale a single core instance of Concrete5 through using separate databases.

It would be great to get people’s views on this as I’m sure there are areas to improve or things that I’ve not considered.  Therefore, please leave a comment with your feedback.

13 thoughts on “A Multi-Site Approach in Concrete5

  1. Danny Baggs

    Hi Alan, your welcome and thanks for taking the time to provide this feedback.

    There may be smarter and better ways or improvements to this so feedback and discussion is very important for the greater good of “Best Practice”.

    Feel free to let me know if you have any questions.

    Dan.

    Reply
  2. DameryWorld

    OK, so this is if you are hosted on a Linux system and have access to the command line? You are just using the Linus link command to link to the default directory….else what?

    Reply
    1. Danny Baggs

      Hi DameryWorld,

      You are correct and you have summarised the approach perfectly.

      Yes, it depends on the Linux link command but allows for a central core and multiple sites from that core.
      The domain mapper add-on in the marketplace takes a different approach if you wanted to share users and administration across many sites in one db.

      As for non linux, I wouldn’t know for sure but think you can do a similar symlink thing through the disk manager.

      Hope that answers your question.

      Regards,

      Dan

      Reply
    1. Danny Baggs

      Sorry Brendan, I don’t quite follow the question and what you mean specifically with “incorporate this into concrete5″. Can you re-phrase the question?

      Regards,

      Dan

      Reply
      1. Brendan

        Dan,
        I now have two sites running off the same core as described above. I have installed Tera-WURFL on my server. I would like to redirect site visitors using mobile devices to the mobile site instead of the desktop site. Should I be adding some code to the header file of my desktop site to check the user device and if they are on a mobile device and send them to the mobile site? My apologies, I thought this part of your thread referenced the Tera-WURFL installation.

        Regards, Brendan

        Reply
        1. Danny Baggs

          Hi Brendan,

          That certainly makes more sense.

          Yes, you need to add the Tera WURFL detection script into your pages as the very first thing that runs. An example of the Tera WURFL site is given here: http://bag.gs/fUYNCm.

          To give you a real example however, this was my script using Tera WURFL that I included at the top of every page. I’ve since moved on from Tera WURFL and use DeviceAtlas instead but the principle remains the same:


          <?php
          // Include the Tera-WURFL file
          require_once('/var/www/default/Tera-Wurfl/TeraWurfl.php');

          if($_REQUEST['ignore'])
          $_SESSION['ignore']=$_REQUEST['ignore'];

          if(!isset($_SESSION['ignore']))
          {
          $wirelessExceptions = array("Apple:iPad");

          // instantiate the Tera-WURFL object
          $wurflObj = new TeraWurfl();

          // Get the capabilities of the current client.
          $matched = $wurflObj->getDeviceCapabilitiesFromAgent();

          $isDevice = $wurflObj->getDeviceCapability("is_wireless_device");
          $deviceBrand = $wurflObj->getDeviceCapability("brand_name");
          $deviceModel = $wurflObj->getDeviceCapability("model_name");
          $deviceIdentifier = $deviceBrand . ':' . $deviceModel;
          $isException = in_array($deviceIdentifier,$wirelessExceptions);

          // see if this client is on a wireless device (or if they can't be identified)
          if($wurflObj->getDeviceCapability("is_wireless_device") && !$isException)
          {
          $urlHeader = "Location: http://m.domain.com" . $c->getCollectionPath();
          header($urlHeader);
          }
          }
          ?>

          and the following was included at the top of my mobile site pages:


          <?php
          // Include the Tera-WURFL file
          require_once('/var/www/default/Tera-Wurfl/TeraWurfl.php');

          if($_REQUEST['ignore'])
          $_SESSION['ignore']=$_REQUEST['ignore'];

          if($_REQUEST['profile'])
          {
          session_destroy();
          $_SESSION['profileOverride']=$_REQUEST['profile'];
          unset($_SESSION['profile']);
          }

          if(!isset($_SESSION['profile']))
          {

          // instantiate the Tera-WURFL object
          $wurflObj = new TeraWurfl();

          // Get the capabilities of the current client.
          $matched = $wurflObj->getDeviceCapabilitiesFromAgent();

          // see if this client is on a wireless device (or if they can't be identified)
          if(!$wurflObj->getDeviceCapability("is_wireless_device") && !$_SESSION['ignore'])
          {
          // Redirect to "desktop" domain and tell the checking script to ignore the user agent detection
          header("Location: http://www.domain.com?ignore=true");
          }
          else
          {
          // Let's build up some capabilities to judge the device by
          $_SESSION['deviceBrand']=$wurflObj->getDeviceCapability("brand_name");
          $_SESSION['deviceModel']=$wurflObj->getDeviceCapability("model_name");
          $_SESSION['deviceModelExtra']=$wurflObj->getDeviceCapability("model_extra_info");
          $_SESSION['deviceResolutionWidth']=$wurflObj->getDeviceCapability("resolution_width");
          $_SESSION['deviceResolutionHeight']=$wurflObj->getDeviceCapability("resolution_height");
          $_SESSION['devicePhysicalScreenWidth']=$wurflObj->getDeviceCapability("physical_screen_width");
          $_SESSION['devicePhysicalScreenHeight']=$wurflObj->getDeviceCapability("physical_screen_height");
          $_SESSION['deviceDualOrientation']=$wurflObj->getDeviceCapability("dual_orientation");
          $_SESSION['deviceMaxImageWidth']=$wurflObj->getDeviceCapability("max_image_width");
          $_SESSION['deviceMaxImageHeight']=$wurflObj->getDeviceCapability("max_image_height");
          $_SESSION['viewportWidth']=$wurflObj->getDeviceCapability("viewport_width");
          $_SESSION['viewportInitialScale']=$wurflObj->getDeviceCapability("viewport_initial_scale");
          $_SESSION['viewportSupported']=$wurflObj->getDeviceCapability("viewport_supported");
          $_SESSION['deviceCSSRounded']=$wurflObj->getDeviceCapability("css_rounded_corners");

          // What are the categories?
          if(!isset($_SESSION['profile']))
          {
          ////////////////////////////////////////
          // Category 1: Default Device Context //
          ////////////////////////////////////////

          // Default Device Context:
          // Usable Screen Width: 120 pixels, minimum.
          // Markup Language Support: XHTML Basic 1.1 [XHTML-Basic] delivered with content type application/xhtml+xml.
          // Character Encoding: UTF-8 [UTF-8].
          // Image Format Support: JPEG, GIF 89a.
          // Maximum Total Page Weight: 20 kilobytes.
          // Colors: 256 Colors, minimum.
          // Style Sheet Support: CSS Level 1 [CSS]. In addition, CSS Level 2 [CSS2] @media rule together with the handheld and all media types (see CSS 2 Media Types).
          // HTTP: HTTP/1.0 [HTTP1.0] or more recent [HTTP1.1].
          // Script: No support for client side scripting.
          $_SESSION['profile'] = PROFILE_BASIC;

          // Apply some defaults if needed
          if(is_null($_SESSION['deviceResolutionWidth']) || $_SESSION['deviceResolutionWidth']==0 || $_SESSION['deviceResolutionWidth']=="")
          {
          $_SESSION['deviceResolutionWidth'] = 120;
          }
          if(is_null($_SESSION['deviceResolutionHeight']) || $_SESSION['deviceResolutionHeight']==0 || $_SESSION['deviceResolutionHeight']=="")
          {
          $_SESSION['deviceResolutionHeight'] = 300;
          }

          //////////////////////////////////////////////////////////////////
          // Category 2: Screen resolution greater than or equal to 240px //
          //////////////////////////////////////////////////////////////////
          if($_SESSION['deviceResolutionWidth']>240)
          {
          $_SESSION['profile'] = PROFILE_INTERMEDIATE;
          }

          ////////////////////////////////////
          // Category 3: Support of CSS etc //
          ////////////////////////////////////
          if($_SESSION['deviceResolutionWidth']>240 && $_SESSION['deviceCSSRounded']!="none")
          {
          $_SESSION['profile'] = PROFILE_ADVANCED;
          }
          }

          if($_SESSION['profileOverride'] && $_SESSION['profileOverride']!="reset")
          {
          $_SESSION['profile']=$_SESSION['profileOverride'];
          }
          }
          }
          ?>

          The good thing about this approach is that you can classify devices into profiles based on certain criteria of the device and deliver the appropriate experience as a result.

          Hope this helps,

          Dan

          Reply
  3. brody

    so every time you update to a new version of concrete you update (…:/var/www/example.com$ ln -s ../default/concrete5.4.0.5/concrete concrete) for each website?

    if that is the case, i will be doing lots of updates if i have hundreds of sites.

    Reply
    1. Danny Baggs

      Hi Brody,

      Yes, that is the case as the symlink will point to a specific version of the core. Of course, if you were keen not to repeat this manually, you could write a little script. However, as each of the sites that I’ve built and admin for my clients have different functionality, this process is just one step in my overall upgrade and test approach.

      I would be happy to have shortcomings on this approach pointed out so that true best-practice evolves. Therefore, if you have further ideas on this one then please feel free to comment further.

      Kind regards,

      Dan

      Reply
    1. Danny Baggs

      I tend to use PuTTY (http://bag.gs/kD7rWc) from a windows environment. I also use Ubuntu desktop so sometimes use the native ssh within the OS there too.

      I’m afraid that I don’t really know what vPuTTY or PSCP is.

      Regards,

      Dan

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>