In a recent project I was confronted with a technology challenge in Concrete that had "fun" and "pain" written all over it. The task was to produce a website that has multiple themes; one generic, one we'll call "rockets" and one we'll call "torpedoes". Upon entering the site, a user could chose to view information about rockets, torpedoes, or they could view a small handful of generic pages. The trick was this... Once they viewed either rockets or torpedoes, all of the generic pages should from that point assume the theme of either rockets or torpedoes?
Confusing? Kind of... The motivation was, once the user chooses a vertical, their experience should remain consistent across all pages unless they switch verticals.
I got my trusty friend MIRV involved as nobody outside of Concrete knows the core like he does. He quickly pointed out that we could easily set the theme on the fly using site_theme_paths and the setTheme() function off the view object.
That part was simple:
$v = View::getInstance();
$v->setTheme("torpedoes");
The next trick was to come up with a way to indicate which theme should be used. It was pretty simple to employ a cookie here. This only added one line of code:
$useTheme = $_COOKIE['useTheme'];
$v = View::getInstance();
$v->setTheme($useTheme);
There was an immediate problem. We could no longer access login or the dashboard as it was using a theme based on the cookie. This process had naturally hijacked the whole theme functionality of Concrete5.
The next riddle was to come up with a way to determine what pages actually qualify for theme switching. Our first pass was to investigate the REQUEST_URI environment variable and select it based off of that. This had two major drawbacks. The first is that if pages weren't carefully added to the site, they might fall out of our path-based rules and fall back to the wrong theme. The second was that this was heavily dependent on pretty url's. Anybody with a good amount of Concrete5 experience knows that through editing pages, you often fall out of pretty URL's back to form GET driven URL's.
After repeatedly smashing my head against a wall, I started to investigate how I could determine what theme the requested page uses by default. If I could determine that it was the generic theme, then I'd know that I need to switch this theme out when our cookie is set. With only two possible URL configurations, this wasn't too hard.
if( preg_match("/cID=(\d+)/", $_SERVER['REQUEST_URI'], $matches) ) {
$cID = $matches[1];
$page = Page::getByID($cID);
} else {
$page_path = preg_replace(array("#" . DIR_REL . "#", "#/$#"), '', $_SERVER['REQUEST_URI']);
if( $page_path == '/' ) {
$page = Page::getByID(1);
} else {
$page = Page::getByPath($page_path);
}
}
$theme_handle = $page->getCollectionThemeObject()->getThemeHandle();
Though there's probably a prettier way to grab the Page object with pretty url's, this was the most consistent method I arrived at, and now we had our theme handle stored in a variable for inspection!
The last part of the site_theme_paths puzzle was to act on this theme handle like so:
if($theme_handle == "generic" && !preg_match("/login/", $_SERVER['REQUEST_URI'])) {
$v->setTheme($useTheme);
}
You'll notice that I had to do an extra check to see if we are at the login page. I'm not sure why the login page meets the requirement of having the generic theme. It's not because of the name "generic" as in my application my theme wasn't named "generic". Rather than dig deep to figure this out, I just filtered it out with a regex match.
All stitched together, my site_theme_paths.php file looked like:
/* Look for a theme to use based on the useTheme cookie */
$useTheme = $_COOKIE['useTheme'];
if (!is_null($useTheme)) {
if( preg_match("/cID=(\d+)/", $_SERVER['REQUEST_URI'], $matches) ) {
$cID = $matches[1];
$page = Page::getByID($cID);
} else {
$page_path = preg_replace(array("#" . DIR_REL . "#", "#/$#"), '', $_SERVER['REQUEST_URI']);
if( $page_path == '/' ) {
$page = Page::getByID(1);
} else {
$page = Page::getByPath($page_path);
}
}
$theme_handle = $page->getCollectionThemeObject()->getThemeHandle();
if(preg_match("/phillips_generic/", $theme_handle) && !preg_match("/login/", $_SERVER['REQUEST_URI'])) {
$v->setTheme($useTheme);
}
}
The next step was to come up with a very basic javascript library to set this cookie. I could have gotten much fancier than this, but I had MIRV create a simple function that I could run from anywhere to set the cookie.
function setTheme(themeName) {
var expires = 60 * 5; //5 minutes
if (themeName != "" && themeName != null) {
document.cookie = "useTheme="+ themeName +
";expires=" + expires +
";path=/";
}
}
Then, in my torpedo and rocket themes, I had this bit of javascript:
$(document).ready(function() {
setTheme('rocket');
});
The end result? If you hit any rocket or torpedo page, the cookie gets set. From then on, any generic pages you visit will assume the proper theme. If you switch sides, no problem, the cookie updates and you get the new theme.
In the end, we were able to prove the concept and see that it wasn't what the client wanted after all, but I thought I'd give it a home on the Internet so that others might find it.
Please add a comment
Leave a Reply