Minisites in Open Atrium
A single Open Atrium installation to serve minisites from different domains with a common backend
You can use a single Open Atrium installation to create minisites, i.e., minimal websites with a simple structure, served at different domains, but sharing a centralized backend. Each minisite can be public or private and can have a specific theme and specific features. An anonymous visitor will only see a small, self-contained site, while site editors will be able to manage content on all minisites from the same backend.
This is an ideal situation, for example, when an Open Atrium installation is used as a private Intranet but you want to build small auxiliary sites for conferences or events, and possibly take advantage of the fact that editors and registered users are the same as the main Intranet.
And, best of all, this can be done with a minimal amount of coding and respecting the Open Atrium architecture.
Creating and Customizing a Minisite
Our aim is to build a feature that will allow us to create and customize a minisite in just a few minutes. We want to start from the ordinary "Create Group" page:
And this is what we get immediately upon form submission:
The minisite ships with default content (pages linked from the left side menu) and the editor can just click and add content.
We can also hook our minisite to the built-in look-and-feel customization that Open Atrium provides for its groups and give the possibility to the editor to choose background and foreground colors:
After a quick content editing, the editor can achieve something like this:
Screenshots are taken from Alfa Puentes, an international project funded by the EU that will focus on exchanging best practices between Europe and Latin America about higher education and that will include several events and conferences, each with a dedicated minisite.
Let's now see how to build this feature in Open Atrium.
Creating a minisite group type (spaces preset)
To start, we simply define a new spaces preset type, named "minisite": this can be done through hook_spaces_presets()
using the existing definitions for public and private spaces from atrium_groups.spaces.inc
as a basis.
<span style="color: #000000"><span style="color: #0000BB"><?php<br><br></span><span style="color: #007700">function </span><span style="color: #0000BB">atrium_minisite_spaces_presets</span><span style="color: #007700">() {<br> </span><span style="color: #0000BB">$export </span><span style="color: #007700">= array();<br><br> </span><span style="color: #0000BB">$spaces_presets </span><span style="color: #007700">= new </span><span style="color: #0000BB">stdClass</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">disabled </span><span style="color: #007700">= </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">api_version </span><span style="color: #007700">= </span><span style="color: #0000BB">3</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">name </span><span style="color: #007700">= </span><span style="color: #DD0000">'minisite'</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">title </span><span style="color: #007700">= </span><span style="color: #DD0000">'Minisite'</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">description </span><span style="color: #007700">= </span><span style="color: #DD0000">'Minisite space preset.'</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">space_type </span><span style="color: #007700">= </span><span style="color: #DD0000">'og'</span><span style="color: #007700">;<br> ...<br></span><span style="color: #0000BB">?></span></span>
The new space preset can be exported into a feature and creating a minisite becomes as easy as creating a new group.
Assigning features to a minisite
Just like a standard Open Atrium group has some features available by default, we will want our minisites to use a certain set of features by default. In our case, we built two features for Pages and News and we enable them for our minisites, that will thus have static pages and news available.
<span style="color: #000000"><span style="color: #0000BB"><?php<br><br> </span><span style="color: #007700">... (</span><span style="color: #0000BB">follows from above</span><span style="color: #007700">) ...<br> </span><span style="color: #0000BB">$spaces_presets</span><span style="color: #007700">-></span><span style="color: #0000BB">value </span><span style="color: #007700">= array(<br> </span><span style="color: #DD0000">'variable' </span><span style="color: #007700">=> array(<br> </span><span style="color: #DD0000">'spaces_features' </span><span style="color: #007700">=> array(<br> </span><span style="color: #DD0000">'atrium_blog' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_book' </span><span style="color: #007700">=> </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_calendar' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_casetracker' </span><span style="color: #007700">=> </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_members' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_pages' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_shoutbox' </span><span style="color: #007700">=> </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'spaces_dashboard' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'atrium_news' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br> ),<br> ... (continue </span><span style="color: #0000BB">along the lines of atrium_groups</span><span style="color: #007700">.</span><span style="color: #0000BB">spaces</span><span style="color: #007700">.</span><span style="color: #0000BB">inc</span><span style="color: #007700">) ...<br></span><span style="color: #0000BB">?></span></span>
Making minisites self-contained: menus and blocks
We want our minisites to be self-contained as much as possible. In order to achieve that, with some standard Drupal hooks, we create a new menu for each minisite, that takes care of updating it as soon as the respective minisite group changes. Here is one of the implemented hooks:
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">atrium_minisite_nodeapi</span><span style="color: #007700">(&</span><span style="color: #0000BB">$node</span><span style="color: #007700">, </span><span style="color: #0000BB">$op</span><span style="color: #007700">, </span><span style="color: #0000BB">$arg </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">) {<br> <br> if (isset(</span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">spaces_preset_og</span><span style="color: #007700">) &&<br> </span><span style="color: #0000BB">atrium_minisite_is_minisite_preset</span><span style="color: #007700">(</span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">spaces_preset_og</span><span style="color: #007700">)) {<br> if (</span><span style="color: #0000BB">in_array</span><span style="color: #007700">(</span><span style="color: #0000BB">$op</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'insert'</span><span style="color: #007700">, </span><span style="color: #DD0000">'update'</span><span style="color: #007700">, </span><span style="color: #DD0000">'delete'</span><span style="color: #007700">))) {<br> </span><span style="color: #0000BB">$menu </span><span style="color: #007700">= array();<br> </span><span style="color: #0000BB">$menu</span><span style="color: #007700">[</span><span style="color: #DD0000">'title'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">title</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$menu</span><span style="color: #007700">[</span><span style="color: #DD0000">'menu_name'</span><span style="color: #007700">] = </span><span style="color: #0000BB">atrium_minisite_get_menu_name</span><span style="color: #007700">(</span><span style="color: #0000BB">$node</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$menu</span><span style="color: #007700">[</span><span style="color: #DD0000">'description'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">og_description</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">atrium_minisite_menu_api</span><span style="color: #007700">(</span><span style="color: #0000BB">$op</span><span style="color: #007700">, </span><span style="color: #0000BB">$menu</span><span style="color: #007700">);<br> <br> if (</span><span style="color: #0000BB">$op </span><span style="color: #007700">== </span><span style="color: #DD0000">'insert'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">module_invoke_all</span><span style="color: #007700">(</span><span style="color: #DD0000">'atrium_minisite_default_content'</span><span style="color: #007700">, </span><span style="color: #0000BB">$node</span><span style="color: #007700">, </span><span style="color: #0000BB">$menu</span><span style="color: #007700">);<br> }<br> }<br> } <br>}<br></span><span style="color: #0000BB">?></span></span>
As you can notice, our minisite feature exposes an atrium_minisite_default_content()
hook which allows other modules to provide default content once a new minisite is created (e.g. "About" and "Contact" pages, etc...). Shipping each new minisite with default content allows the editor to start filling up the site straight away without the need to recreate again and again the same content structure.
To make our minisite menu group-aware, we implement hook_block()
in atrium_minisites.module
. The implementation below relies on a couple of helper functions defined elsewhere.
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">atrium_minisite_block</span><span style="color: #007700">(</span><span style="color: #0000BB">$op </span><span style="color: #007700">= </span><span style="color: #DD0000">'list'</span><span style="color: #007700">, </span><span style="color: #0000BB">$delta </span><span style="color: #007700">= </span><span style="color: #0000BB">NULL</span><span style="color: #007700">, </span><span style="color: #0000BB">$edit </span><span style="color: #007700">= </span><span style="color: #0000BB">NULL</span><span style="color: #007700">) {<br> <br> </span><span style="color: #0000BB">$blocks </span><span style="color: #007700">= array();<br> if (</span><span style="color: #0000BB">$op </span><span style="color: #007700">== </span><span style="color: #DD0000">'list'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'primary-navigation'</span><span style="color: #007700">][</span><span style="color: #DD0000">'info'</span><span style="color: #007700">] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Minisite: Primary navigation'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'primary-navigation'</span><span style="color: #007700">][</span><span style="color: #DD0000">'cache'</span><span style="color: #007700">] = </span><span style="color: #0000BB">BLOCK_NO_CACHE</span><span style="color: #007700">;<br><br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'secondary-navigation'</span><span style="color: #007700">][</span><span style="color: #DD0000">'info'</span><span style="color: #007700">] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Minisite: Secondary navigation'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'secondary-navigation'</span><span style="color: #007700">][</span><span style="color: #DD0000">'cache'</span><span style="color: #007700">] = </span><span style="color: #0000BB">BLOCK_NO_CACHE</span><span style="color: #007700">;<br><br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'full-navigation'</span><span style="color: #007700">][</span><span style="color: #DD0000">'info'</span><span style="color: #007700">] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Minisite: Full navigation'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'full-navigation'</span><span style="color: #007700">][</span><span style="color: #DD0000">'cache'</span><span style="color: #007700">] = </span><span style="color: #0000BB">BLOCK_NO_CACHE</span><span style="color: #007700">;<br> }<br> if (</span><span style="color: #0000BB">$op </span><span style="color: #007700">== </span><span style="color: #DD0000">'view'</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">atrium_minisite_is_minisite_space</span><span style="color: #007700">()) {<br> </span><span style="color: #0000BB">$config </span><span style="color: #007700">= </span><span style="color: #0000BB">atrium_minisite_get_menu_block_config</span><span style="color: #007700">(</span><span style="color: #0000BB">$delta</span><span style="color: #007700">);<br> if (</span><span style="color: #0000BB">$delta </span><span style="color: #007700">== </span><span style="color: #DD0000">'secondary-navigation' </span><span style="color: #007700">|| </span><span style="color: #0000BB">$delta </span><span style="color: #007700">== </span><span style="color: #DD0000">'full-navigation'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$config</span><span style="color: #007700">[</span><span style="color: #DD0000">'level'</span><span style="color: #007700">] = </span><span style="color: #0000BB">2</span><span style="color: #007700">; <br> </span><span style="color: #0000BB">$config</span><span style="color: #007700">[</span><span style="color: #DD0000">'expanded'</span><span style="color: #007700">] = </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br> </span><span style="color: #0000BB">$config</span><span style="color: #007700">[</span><span style="color: #DD0000">'depth'</span><span style="color: #007700">] = </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br> }<br> if (</span><span style="color: #0000BB">$delta </span><span style="color: #007700">== </span><span style="color: #DD0000">'full-navigation'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$config</span><span style="color: #007700">[</span><span style="color: #DD0000">'level'</span><span style="color: #007700">] = </span><span style="color: #0000BB">1</span><span style="color: #007700">; <br> }<br> </span><span style="color: #0000BB">$blocks </span><span style="color: #007700">= </span><span style="color: #0000BB">menu_tree_build</span><span style="color: #007700">(</span><span style="color: #0000BB">$config</span><span style="color: #007700">);<br> unset(</span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'subject'</span><span style="color: #007700">]);<br> }<br> }<br> return </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?></span></span>
This block is then specified as a context reaction, so that site visitors will see a self-contained navigation when browsing the minisite.
Making minisites themeable
Each minisite can be assigned a different theme. To do so from a convenient interface, we implement hook_form_alter()
in atrium_minisite.module
to give the possibility to select a theme for the given minisite. Again, we omit some helper functions for brevity.
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #FF8000">/**<br> * Implementation of hook_form_alter()<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">atrium_minisite_form_alter</span><span style="color: #007700">(&</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_id</span><span style="color: #007700">) {<br> <br> if (isset(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#node'</span><span style="color: #007700">]) && </span><span style="color: #0000BB">$form_id </span><span style="color: #007700">== </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#node'</span><span style="color: #007700">]-></span><span style="color: #0000BB">type </span><span style="color: #007700">.</span><span style="color: #DD0000">'_node_form'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$node </span><span style="color: #007700">= </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#node'</span><span style="color: #007700">]; <br> <br> if (</span><span style="color: #0000BB">og_is_group_type</span><span style="color: #007700">(</span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">type</span><span style="color: #007700">)) {<br> </span><span style="color: #0000BB">module_load_include</span><span style="color: #007700">(</span><span style="color: #DD0000">'inc'</span><span style="color: #007700">, </span><span style="color: #DD0000">'system'</span><span style="color: #007700">, </span><span style="color: #DD0000">'system.admin'</span><span style="color: #007700">); <br> </span><span style="color: #0000BB">atrium_minisite_theme_form</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">);<br> }<br> <br> if (</span><span style="color: #0000BB">atrium_minisite_is_minisite_space</span><span style="color: #007700">()) {<br> if (</span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">type </span><span style="color: #007700">== </span><span style="color: #DD0000">'page'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$space </span><span style="color: #007700">= </span><span style="color: #0000BB">spaces_get_space</span><span style="color: #007700">();<br> </span><span style="color: #0000BB">$conf</span><span style="color: #007700">[</span><span style="color: #DD0000">'menu_default_node_menu'</span><span style="color: #007700">] = </span><span style="color: #0000BB">atrium_minisite_get_menu_name</span><span style="color: #007700">(</span><span style="color: #0000BB">$space</span><span style="color: #007700">-></span><span style="color: #0000BB">group</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$menu_name </span><span style="color: #007700">= </span><span style="color: #0000BB">atrium_minisite_get_menu_name</span><span style="color: #007700">(</span><span style="color: #0000BB">$space</span><span style="color: #007700">-></span><span style="color: #0000BB">group</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$menu </span><span style="color: #007700">= array(</span><span style="color: #0000BB">$menu_name </span><span style="color: #007700">=> </span><span style="color: #0000BB">$space</span><span style="color: #007700">-></span><span style="color: #0000BB">group</span><span style="color: #007700">-></span><span style="color: #0000BB">title</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'menu'</span><span style="color: #007700">][</span><span style="color: #DD0000">'parent'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#options'</span><span style="color: #007700">] = </span><span style="color: #0000BB">menu_parent_options</span><span style="color: #007700">(</span><span style="color: #0000BB">$menu</span><span style="color: #007700">, </span><span style="color: #0000BB">$item</span><span style="color: #007700">);<br> }<br> }<br> }<br>}<br></span><span style="color: #0000BB">?></span></span>
We can now choose the best theme for our minisite directly on the creation page:
Serving minisites on different domains
Each Open Atrium group gets its own URL path component; in our case the minisite will be reachable at something like http://alfapuentes.org/conference/. With a quick Persistent URL customization we can actually set the URL rewriting to be per domain, allowing our minisite to have its own domain. This can be achieved by:
- Enabling the "Domain" URL modification type from
admin/settings/purl/types
; - Assigning to "Group space" the new modifier type on
admin/settings/purl
; - Adding a custom URL each time we are going to create a minisite group
By taking advantage of the Persistent URL domain rewriting we can really make the new minisite look completely independent from the underlying Open Atrium platform:
The techniques shown here are covered in detail in our trainings; if interested, contact us for more information.
Tags: Code Driven Development, Drupal Planet, Open AtriumImage: