I got stuck with some modifications and maybe someone could give me some help.

What am I doing

I use the multiple theme plugin to switch themes for my WordPress project. To switch between the themes a URL Parameter is added www.mydomain/?parameter=XYZ. So far everything works great.

Where is the issue

The problem is when I click on internal links within the page I am sent back to the basic theme as the links and URLS within the website do not have the URL parameter appended.

Please note that I am aware that the plugin has a sticky function that adds the parameter to most URLs but it does not work for all URLS and especially not those which are loaded with AJAX. Also I still need to be able to access the basic theme via the normal URL without parameters.

What I am trying to do

Within the functions.php of the specific theme I try to add a function that adds the needed parameter to all URLs that WordPress is generating.

Based on this info:
I am in the position to at least echo the right URL

//Add partner parameter
echo add_query_arg( 'partner', 'XYZ', get_permalink());

Unfortunately I have no clue how I may turn that into a function that all URLS within the loaded webpage get the parameter appended? Does anyone have an idea?

What I already checked

I am Looking to append URL Parameter to all URLs

As it is a redirection and not a rewrite of the URL I can't then access the normal URL without the parameter anymore.


You can set the $_GET global internally. By saving the parameter passed as user meta and/or a cookie and retrieving it later. This would probably have to be put in your /wp-content/mu-plugins/ folder to work properly (so it is loaded early):


    $parameter = 'theme'; // default for theme test drive
    $savemethod = 'both'; // 'usermeta' (only), 'cookie' (only) or 'both'
    $expires = 7*24*60*60; // cookie length (a week in seconds)

    if (!function_exists('is_user_logged_in')) {require(ABSPATH.WPINC.'/pluggable.php');}
    if (is_user_logged_in()) {global $current_user; wp_get_current_user();}

    if (isset($_GET[$parameter])) {
        // sanitize querystring input (like a theme slug)
        $originalvalue = $_GET[$parameter];
        $value = sanitize_title($_GET[$originalvalue]);
        if (!$value) {$value = '';}

        // save parameter as usermeta
        if ( ($savemethod != 'cookie') && (is_user_logged_in()) ) {
            if ($value == '') {delete_user_meta($current_user->ID,$parameter);}
            else {update_user_meta($current_user->ID,$parameter,$value);}
        // save parameter value as cookie
        if ($savemethod != 'usermeta') {
            if ($value == '') {setCookie($parameter,'',-300);}
            else {setCookie($parameter,$value,time()+$expires);}
    elseif ( ($savemethod != 'cookie') && (is_user_logged_in()) ) {
        // maybe set parameter from usermeta
        $uservalue = get_user_meta($current_user->ID,$parameter,true);
        if ($uservalue != '') {$_GET[$parameter] = $uservalue;}
    elseif ($savemethod != 'usermeta') {
        // maybe set parameter from cookie
        if ( (isset($_COOKIE[$parameter])) && ($_COOKIE[$parameter] != '') ) {
            $_GET[$parameter] = $_COOKIE[$parameter];

    // parameter override debugging
    if ( (isset($_GET[$parameter.'debug'])) && ($_GET[$parameter.'debug'] == '1') ) {
        $debugfile = get_stylesheet_directory().'/'.$parameter.'-debug.txt';
        $fh = fopen($debugfile,'a'); 
        $debugline = $_SERVER['REQUEST_URI'].'::original:'.$originalvalue;
        $debugline .= '::cookie:'.$_COOKIE[$parameter'].'::usermeta.'.$uservalue;
        $debugline .= '::current:'.$_GET[$parameter].PHP_EOL;
        fwrite ($debugfile,$debugline); fclose($fh);


Usage: This could be used for making Theme Test Drive persistent on a per-user level, currently it only works on a user-role level and/or via non-persistent querystring. However, Theme Test Drive does not allow for site area selection - either the whole theme is switched for the user everywhere or nowhere.

  • Thanks @majick. I added following code if(!function_exists('is_user_logged_in')) require (ABSPATH . WPINC . '/pluggable.php'); to avoid a fatal error and put it in mu-plugins but it does not seem to work yet. Any idea where I might miss something? – Lens May 23 '16 at 14:12
  • hard to say at this point it might depend on how the plugin works, but I have this code for a similar (but not quite the same) purpose working fine. is multiple themes or theme test drive? – majick May 23 '16 at 16:02
  • It's the Jonradio multiple themes I use, I'll have a look at theme test drive maybe its more suitable and achieves what I need out of the box :). I let you know. Thanks! – Lens May 23 '16 at 17:01
  • Did a quick check with Theme Test Drive, but it does not work either. The parameters are not added to the Urls on the website. Also I noted that the above code locks me out from wp-admin. – Lens May 23 '16 at 17:11
  • what is the parameter? I know theme test drive uses theme which is not necessarily a good thing since it is used in wp-admin on customizer and theme editor page, so perhaps the other has a possible conflict also... – majick May 24 '16 at 3:31

Okay this is one insanely long answer, but fortunately I was working on testing Theme Test Drive plugin with my theme I figured I may as well make it better compatible with Multiple Themes also by having the admin issue fixed...

So here is the code I came up with that handles both. Again, put it in your /wp-content/mu-plugins/ folder (though it probably be fine as a plugin too.) I am putting it in my theme to support Child Theme switching anyway, but it needs to load earlier than that to support Parent Theme switching.

I will add some other notes later, for the now it is worth mentioning that you want to not use the "sticky" option of Multiple Themes as this will cause conflicts - because if you have two themes on the same site open in different windows the persistant cookie will override the matching admin calls, and this is exactly why I have coded this solution - to overcome that problem.

Basically it let's Multiple Themes do the work of determining which stylesheet/template to use as it normally would, and stores a list of URLs with different theme to the default when they are loaded, then matches to these using the referrer string of the admin AJAX request (both admin-ajax.php and admin-post.php supported.)

As for Theme Test Drive I found that it already loads in admin if you have the persistent theme drive enabled, fine. But for the querystring test drive I have used an entirely different approach with matching cookies and transients.

Anyway, that's enough of that, read the code comments for more (if you dare.) Otherwise, I have tested it rather thoroughly already so you can just give it a go... :-)


if (!function_exists('muscle_theme_switch_admin_fix')) {
 function muscle_theme_switch_admin_fix() {

    $multiplethemes = false; $themetestdrive = false; $debug = false;
    $expires = 24*60*60; // transient time for temporary theme test drive (querystring)

    // maybe reset cookie and URL data by user request
    if ( (isset($_GET['resetthemes'])) && ($_GET['resetthemes'] == '1') ) {
        if ($debug) {echo "<!-- THEME SWITCH DATA RESET -->";}
        if ($themetestdrive) {setCookie('theme_test_drive','',-300);}
        delete_option('theme_switch_request_urls'); return;

    // maybe set debug switch
    if ( (isset($_GET['debug'])) && ($_GET['debug'] == '1') ) {$debug = true;}
    elseif (defined('THEMEDEBUG')) {$debug = THEMEDEBUG;}

    // check for a valid active plugin
    $activeplugins = maybe_unserialize(get_option('active_plugins'));
    if (!is_array($activeplugins)) {return;}
    if (in_array('jonradio-multiple-themes/jonradio-multiple-themes.php',$activeplugins)) {$multiplethemes = true;}
    if (in_array('theme-test-drive/themedrive.php',$activeplugins)) {$themetestdrive = true;}
    if ( (!$multiplethemes) && (!$themetestdrive) ) {return;} // nothing to do

    // theme test drive by default only filters via get_stylesheet and get_template
    // improve theme test drive to use options filters like multiple themes instead
    if ($themetestdrive) {
        remove_filter('template', 'themedrive_get_template'); remove_filter('stylesheet', 'themedrive_get_stylesheet');
        add_filter('pre_option_stylesheet', 'themedrive_get_stylesheet'); add_filter('pre_option_template', 'themedrive_get_template');

    // maybe load stored alternative theme for AJAX/admin calls
    if (is_admin()) {

        // get pagenow to check for admin-post.php as well
        global $pagenow;

        if ( ( (defined('DOING_AJAX')) && (DOING_AJAX) ) || ($pagenow == 'admin-post.php') ) {

            // set the referer path for URL matching
            $referer = parse_url($_SERVER['HTTP_REFERER'],PHP_URL_PATH);

            // set some globals for the AJAX theme options
            global $ajax_stylesheet, $ajax_template;

            // check for temporary Theme Test Drive cookie data
            if ( ($themetestdrive) && (isset($_COOKIE['theme_test_drive'])) && ($_COOKIE['theme_test_drive'] != '') ) {
                $i = 0; $cookiedata = explode(',',$_COOKIE['theme_test_drive']);
                // attempt to match referer data with stored transient request
                foreach ($cookiedata as $transientkey) {
                    $transientdata = get_transient($transientkey);
                    if ($transientdata) {
                        $data = explode(':',$transientdata);
                        if ($data[0] == $referer) {
                            $ajax_stylesheet = $data[1]; $ajax_template = $data[2];
                            $transientdebug = $transientdata; $matchedurlpath = true;
            elseif ($multiplethemes) {
                // check the request URL list to handle all other cases
                $requesturls = get_option('theme_switch_request_urls');
                if (!is_array($requesturls)) {return;}

                if ( (is_array($requesturls)) && (array_key_exists($referer,$requesturls)) ) {
                    $matchedurlpath = true;
                    $ajax_stylesheet = $requesturls[$referer]['stylesheet'];
                    $ajax_template = $requesturls[$referer]['template'];

            if ($matchedurlpath) {
                // add new theme option filters for admin AJAX (and admin post)
                // so any admin actions defined by the theme are finally loaded!

                function admin_ajax_stylesheet() {global $ajax_stylesheet; return $ajax_stylesheet;}
                function admin_ajax_template() {global $ajax_template; return $ajax_template;}

            // maybe output debug info for AJAX/admin test frame
            if ($debug) {
                echo "<!-- COOKIE DATA: ".$_COOKIE['theme_test_drive']." -->";
                echo "<!-- TRANSIENT DATA: ".$transientdebug." -->";
                echo "<!-- REFERER: ".$referer." -->";
                echo "<!-- STORED URLS: "; print_r($requesturls); echo " -->";
                if ($matchedurlpath) {echo "<!-- URL MATCH FOUND -->";} else {echo "<!-- NO URL MATCH FOUND -->";}
                echo "<!-- AJAX Stylesheet: ".get_option('stylesheet')." -->";
                echo "<!-- AJAX Template: ".get_option('template')." -->";

            return; // we are done so bug out here

    // store public request URLs where an alternate theme is active
    // (multiple themes does not load in admin, but theme test drive does)
    if ( ($themetestdrive) || ( (!is_admin()) && ($multiplethemes) ) ) {

        // get current theme (possibly overriden) setting
        $themestylesheet = get_option('stylesheet'); $themetemplate = get_option('template');

        // remove filters, get default theme setting, re-add filters
        if ($multiplethemes) {
            remove_filter('pre_option_stylesheet', 'jr_mt_stylesheet'); remove_filter('pre_option_template', 'jr_mt_template');
            $stylesheet = get_option('stylesheet'); $template = get_option('template');
            add_filter('pre_option_stylesheet', 'jr_mt_stylesheet'); add_filter('pre_option_template', 'jr_mt_template');
        if ($themetestdrive) {
            // note: default theme test drive filters are changed earlier on
            remove_filter('pre_option_stylesheet', 'themedrive_get_stylesheet'); remove_filter('pre_option_template', 'themedrive_get_template');
            $stylesheet = get_stylesheet(); $template = get_template();
            add_filter('pre_option_stylesheet', 'themedrive_get_stylesheet'); add_filter('pre_option_template', 'themedrive_get_template');

        // set/get request URL values (URL path only)
        $requesturl = parse_url($_SERVER['REQUEST_URI'],PHP_URL_PATH);
        $requesturls = get_option('theme_switch_request_urls');

        // check for a temporary Theme Test Drive (querystring)
        if ( ($themetestdrive) && (isset($_REQUEST['theme'])) && ($_REQUEST['theme'] != '') ) {
             // add a transient style user page cookie for matching later
             $cookiedata = array();
             if ( (isset($_COOKIE['theme_test_drive'])) && ($_COOKIE['theme_test_drive'] != '') ) {
                $existingmatch = false;
                $i = 0; $cookiedata = explode(',',$_COOKIE['theme_test_drive']);
                // remove transient IDs for expired transients
                foreach ($cookiedata as $transientkey) {
                    $transientdata = get_transient($transientkey);
                    if ($transientdata) {
                        $data = explode(':',$transientdata);
                        if ($data[0] == $requesturl) {
                            // update the existing transient data
                            $transientdata = $transientdebug = $requesturl.':'.$themestylesheet.':'.$themetemplate;
                            $existingmatch = true; // no duplicates
                    } else {unset($cookiedata[$i]);}
            // set the matching transient and cookie data
            if (!$existingmatch) {
                 $transientkey = 'theme_test_drive_session_'.uniqid();
                 $transientdata = $transientdebug = $requesturl.':'.$themestylesheet.':'.$themetemplate;
                 set_transient($transientkey, $transientdata, $expires);
                 $cookiedata[] = $transientkey; $cookiedatastring = implode(',',$cookiedata);
                 setCookie('theme_test_drive', $cookiedatastring, time()+$expires);
            // maybe output debug info
            if ($debug) {
                echo "<!-- COOKIE DATA: "; print_r($cookiedata); echo " -->";
                echo "<!-- TRANSIENT DATA: ".$transientdebug." -->";
        elseif ($multiplesthemes) {
            // save/remove the requested URL path in the list
            if ( ($stylesheet == $themestylesheet) && ($template == $themetemplate) ) {
                // maybe remove this request from the stored URL list
                if ( (is_array($requesturls)) && (array_key_exists($requesturl,$requesturls)) ) {
                    if (count($requesturls) === 0) {delete_option('theme_switch_request_urls');}
                    else {update_option('theme_switch_request_urls',$requesturls);}
            else {
                // add this request URL to the stored list
                $requesturls[$requesturl]['stylesheet'] = $themestylesheet;
                $requesturls[$requesturl]['template'] = $themetemplate;
            // maybe output debug info
            if ( (!is_admin()) && ($debug) ) {
                echo "<!-- REQUEST URL: ".$requesturl." -->";
                echo "<!-- STORED URLS: "; print_r($requesturls); echo " -->";

        // maybe output hidden ajax debugging frames
        if ( (!is_admin()) && ($debug) ) {
            echo "<iframe src='".admin_url('admin-ajax.php')."?debug=1' style='display:none;'></iframe>";
            echo "<iframe src='".admin_url('admin-post.php')."?debug=1' style='display:none;'></iframe>";
  • WOW thanks for all the effort @majick. Unfortunately the ajax loaded content still does not want to have the parameter appended. Also Woocommerce is not quite cooperating. I decided to give this issue to a developer and I will be more than happy to post the solution we found. I guess its the theme and plugins I use which make it special (WP Job Manager - Jobify) – Lens May 26 '16 at 10:38
  • BTW I also just switch child themes. – Lens May 26 '16 at 10:43
  • sorry I didn't mention the intention of this code is not to append the parameter, as I don't think that is not reliable, for example you could filter admin_url and append it there, but then if a theme or plugin does admin_url('admin-ajax.php').'?action=some_ajax_action') you would end up with two ? in the URL... ie. admin-ajax.php?theme=themename?action=some_ajax_action ... – majick May 26 '16 at 10:53
  • instead it keeps track of the theme switching so that admin requests loaded from a post/page with a switched theme will also have the matching theme set internally, thus making the theme's AJAX actions available... – majick May 26 '16 at 10:55
  • Ok I understand. The issue is that without the parameter added the theme switches back to the main theme what is wanted. Otherwise I would need to add a parameter to both themes I guess. My current setup: mywebsite.com (Child theme 1) / mywebsite.com/?partner=xyz (Child Theme 2). So as long the parameter is not within the URL it displays the normal theme. As the partner theme is displayed within an iframe (no header/footer) it would break their page layout if the wrong theme is loaded. So I really need that sticky-link part and not an override... – Lens May 26 '16 at 11:36

For your particular use case (as far as I understand it now) you just want to use the sticky queries if the access source is different, thus running on a user level not a site level (eg. such as all posts and/or pages option)...

Since Multiple Themes is actually fine to run "as is" in admin from the sticky query via cookie (it just doesn't do that by default because that would conflict for it's sitewide options)... you can just force it to load for AJAX calls by including select-theme.php...

But since it would be still be good to not have them affect any AJAX calls from the home page where the default theme is used, you can add a list of those paths manually to filter them out (via adding to $defaultthemepaths) This is much easier, but just does not automatically mesh with Multiple Theme's sitewide options, you need to add the default theme URLs manually in this code...

function switched_theme_admin_fix() {

    if (!is_admin()) {return;} // switch-themes.php already loads for non-admin

    // default theme URL paths (add to this list?)
    $defaultthemepaths = array('','/'); // 'home' paths

    // get pagenow to check for admin-post.php as well
    global $pagenow;

    if ( ( (defined('DOING_AJAX')) && (DOING_AJAX) ) || ($pagenow == 'admin-post.php') ) {

        // set the referer path for URL matching
        $referer = parse_url($_SERVER['HTTP_REFERER'],PHP_URL_PATH);

        // do not switch themes if called from a default theme URL path
        if (!in_array($referer,$defaultthemepaths)) {

            // full path to select-theme.php
            $selecttheme = WP_PLUGIN_DIR.'/jonradio-multiple-themes';
            $selecttheme .= '/includes/select-theme.php';

            // just in case multiple themes does update to load for admin
            $included = get_included_files();
            if (!in_array($selecttheme,$included)) {
                // adds the multiple themes stylesheet filters
                if (file_exists($selecttheme) {include($selecttheme);}
  • Thanks Majick, i'll have a look at it. Forgot to activate email notification...so I just saw it now – Lens Jun 2 '16 at 9:57
  • any luck with it? – majick Jun 7 '16 at 2:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.