Allow to switch themes with page caching enabled
cmckay - January 21, 2009 - 03:17
| Project: | Switchtheme |
| Version: | 6.x-1.1 |
| Component: | Code |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | active |
Jump to:
Description
Hi all,
I am having a problem with switchtheme and anonymous users. If I disable normal caching, they are able to switch themes, however when enabled, only authenticated users are permitted. Is this a known limitation of switchtheme? You can see the problem in action at www.myappleguide.com

#1
Hm. Yes, that seems to be a limitation. Drupal's page caching for anonymous users statically caches all pages. Did you try the latest development snapshot already?
#2
Sorry, without further information this issue can only be marked as won't fix. Feel free to re-open this issue if you want to provide further information.
#3
hi,
I am in the process of proposing a CVS account, and I need to show the maintainers where my themes alive. It'll be great if it works for anonymous in normal cache. The latest dev release didn't even work for registered users, so I switched back to the stable one. At least it consistently works for the registered.
So I'd love to know if there's a plan to improve it. Thanks
#4
Hi,
The only workaround I could find is using the ability of Switchteme to set the theme using the $_GET variable. It is'nt really satisfactory nor efficient, but I give it here for what it's worth.
Adding the following in settings.php will pass the theme parameter to all drupal generated links, making them look like example.com/content/mycontent.html?theme=theme_name.
Be aware that :
<?php
function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
if(isset($_GET['theme'])) {
$pos= strpos($_GET['theme'], 'theme'); // a quick extra test to avoid harmful cross-site parameters.
if ($pos === 0) {
$options= array('query' => 'theme='.check_plain($_GET['theme']),);
}
}
}
?>
That should be sufficient to make anonymous users keep a theme.
Going further
As I want to stick with clean urls and don't want duplicate content to arise in search engine, i make a bit of additional rewriting.
Adding the following in settings.php ...
<?php
function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
if(isset($_GET['theme'])) {
$pos= strpos($_GET['theme'], 'theme'); // a quick extra test to avoid harmful parameters (maybe a bit paranoid in this case ?)
if ($pos === 0) {
$path= check_plain($_GET['theme']).'/'.$path ;
}
}
}
?>
... and these lines in .htaccess, BEFORE drupal's own rewriting rules ...
RewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^theme([0-9]+)/(.*)$ index.php?q=$2&theme=theme$1 [L,QSA]
... let me have url looking like 'theme2/content/mycontent.html' and exclude search engines from indexing the themed versions of the site via robots.txt.
You can see it 'in action' at http://www.pascal-morin.com
So, I know this in no way a good and proper solution, but I hope it can be of some use for a few of you.
Pascal
@sun : I'm not sure if this really the best place to post (in fact, I'm quite sure it isn't) but I couldn't think of a more suitable page. So, feel free to edit/move/delete my post. Thanks
#5
Re-opening as feature request.
I'm not entirely sure, but my first guess would be to investigate whether we can use hook_init() to intercept the page request and assign a new theme. Probably needs a separate page cache per theme.
#6
That would for sure increase performance to test the page request in hook_init instead (custom_url_rewrite is called +100 times / page, states the drupal doc).
I hope I'm not missing the point (I'm quite new to drupal API and do not understand well all the hook mechanism), but the main problem I see is that hook_init isn't called on cached pages, so we still have to perform url rewriting to make drupal generate a new page.
Moreover, I did'nt find a way to alter the url besides custom_url_rewrite_in/outbound (the incoming rewriting rules i made with .htaccess can be done that way too), that are not really hooks, as they need to be put in settings.php.
As you're far more experimented than me, you might have a few clues of how this can be done ? I'll try to experiment on all that next week, when I'll have more time.
#7
Thanks for considering this as feature request. It's a great help.
#8
subscribing
#9
I am waiting for this feature as well.
#10
Set a cookie, redirect to that theme (see global redirect). Whats the URL structure for different themes?
#11
@mikeytown2
I am not sure what your question is referring to. But if you meant the structure of the URL for each of the themes, then from what I experienced there is no different at all. The URL remains the same when I switch from one theme to another. So I am not sure if global redirect would be able to help.
#12
@aryanto
Guess this module does a POST to set the theme, If it used a GET, then it would be compatible with caching.
#13
But only if it would implement a custom_url_rewrite_outbound() handler that rewrites all following URLs to use the query string as well.
#14
You could do it ghetto. Check for a cookie in jquery; get URL, if ?theme=* isn't set right, redirect.
#15
As far as I understand, hook_init is not called on cached pages, but hook_but is also called on cached pages. So trying to rewrite the code to use hook_boot could help. I am looking very much forward to a Switchthemes version, that can handle caching.
#16
hook_exit() would be another option. boost_exit might give some inspiration.
<?php
/**
* Implementation of hook_exit(). Performs cleanup tasks.
*
* For POST requests by anonymous visitors, this adds a dummy query string
* to any URL being redirected to using drupal_goto().
*
* This is pretty much a hack that assumes a bit too much familiarity with
* what happens under the hood of the Drupal core function drupal_goto().
*
* It's necessary, though, in order for any session messages set on form
* submission to actually show up on the next page if that page has been
* cached by Boost.
*/
function boost_exit($destination = NULL) {
// Check that hook_exit() was invoked by drupal_goto() for a POST request:
if (!empty($destination) && $_SERVER['REQUEST_METHOD'] == 'POST') {
// Check that we're dealing with an anonymous visitor. and that some
// session messages have actually been set during this page request:
global $user;
if (empty($user->uid) && ($messages = drupal_set_message())) {
// FIXME: call any remaining exit hooks since we're about to terminate?
$query_parts = parse_url($destination);
// Add a nocache parameter to query. Such pages will never be cached
$query_parts['query'] .= (empty($query_parts['query']) ? '' : '&') . 'nocache=1';
// Rebuild the URL with the new query string. Do not use url() since
// destination has presumably already been run through url().
$destination = boost_glue_url($query_parts);
// Do what drupal_goto() would do if we were to return to it:
exit(header('Location: ' . $destination));
}
}
}
?>
Personally I think javascript would be the simple solution. Get it working there first then try to do it in PHP.
http://plugins.jquery.com/project/Cookie
http://plugins.jquery.com/project/parseQuery
IF Cookie.theme != NULL && Query.theme != Cookie.theme THENwindow.location = currentURL + ?theme= + Cookie.theme
ENDIF