5

A client currently has a website set up with three distinct sub-sites (run off a single WordPress install), each with a slightly different theme (same layout, different colour scheme.

I currently have it set up so each sub-site is a page, with all of it's pages as children. The header.php checks which page is the parent and includes the appropriate style.

The problem comes with pages that are identical throughout the site, which the client wishes to have accessible from each sub-site as a separate page.

For example the about page, should be accessible from /site1/about and /site2/about and use the theme for the site that has requested it, but display the same content on both.

I could just create the child pages, and set the rel="canonical" to reference the original, but from a maintenance point of view, it would preferable to have a single page that the content is pulled from on each of the sub sites.

And so to the question, how do I get several URLs/pages to pull their content from a single source?

EDIT: Here's an example of the desired structure:

  • www.example.com - Displays links to the three main services, as well as general information
  • www.example.com/about - Displays information about the client, styled to match www.example.com
  • www.example.com/service1 - Displays navigation related to the specific service, styled in it's own individual colour, alongside generic links (about, contact)
  • www.example.com/service1/about - Displays the navigation and stylesheet for /service1, but using the content from www.example.com/about (and with a canonical link reference to www.example.com/about)

This is then repeated for /service2 and /service3.

4
  • Do you need the features of WordPress Multisite, or is it possible that what you really need is to a single site and to have it to respond differently depending on which domain it is accessed by? Commented Mar 28, 2011 at 3:18
  • The sub-sites are part of the same domain, they simply represent distinct services offered, I'll edit the original post with structure examples. Commented Mar 28, 2011 at 15:15
  • How many pages are like "about"? Just the one, or many? Are they standard for each site, or might some sites have pages "A", "B" and "C" where other sites have "A", "D" and "E"? Will you set these once and the user never change them, or will the users need to flexibly be able to assign and reassign? Will the URL slugs always match across sites? Will "service" be a custom post type (or if currently pages can they be CPTs?) Commented Mar 29, 2011 at 7:20
  • @MikeSchinkel There are several like this, that should be the same across each subsite. (/about, /contact, /testimonials, /invoice-payments, /requirements). Every site will have all of them, and they are a fixed thing, the URLs will be the same so /service1/about, /service2/about. The client doesn't need to be able to make new ones or change them. Thanks! Commented Apr 2, 2011 at 16:36

1 Answer 1

5

I'm still struggling with the exact requirements but if I understand correctly I think you can do to accomplish your goals if you use the code I provide you below and then do the following:

  1. Create a page for each of your services (which it sounds like you have already done): /service1/, /service2/, etc.

  2. Create a "base" service page, with a URL of /base-service/; don't worry, nobody will ever see this on the outside of the website.

  3. Assign /base-service/ as the parent (or grandparent) page for all the "virtual shared" child pages you want replicated under each service.

  4. Create any service specific pages that you also might want to have and assign them to the proper page (i.e. "Service 2", for example.)

  5. Create a page template in your theme for your services called page-service.php and make sure each of your service pages uses that page template (this is needed so we can identify which of your pages are meant to be services).

So here's the code for a class that I called Yoursite_SharedChildPages which you can store in your theme's functions.php file, or in a .php file of a plugin that you might be writing for your site:

if (!class_exists('Yoursite_SharedChildPages')) {
  class Yoursite_SharedChildPages {
    // If wanted we could enhance to allow these values to be defined in an 'init' hook
    static $PAGE_TEMPLATE = 'page-service.php';
    static $ABSTRACT_ROOT = 'base-service';
    static function on_load() {
      // Hook 'request' to route the request correctly
      add_filter('request',array(__CLASS__,'request'));
      // Hook 'page_link' to compose URLs correctly
      add_filter('page_link',array(__CLASS__,'page_link'));
    }
    static function request($query_vars) {
      if (!empty($query_vars['pagename'])) {
        // If the page URL rewrite matched meaning WordPress thinks it's a Page
        // Split the URL path by path segments (i.e. by slashes)
        $pagename = explode('/',$query_vars['pagename']);
        if ($pagename[0] == self::$ABSTRACT_ROOT) {
          // If the first path segment is the abstract root we care about
          if (count($pagename)==1) {
          // Don't allow anyone to visit this abstract root page
            $pagename = array('#'); // An invalid page name, so it will 404
          } else {
            // If it is a child page in which case redirect to the first service
            // This is important so the user can view the page after they edit it
            $pages = self::get_page_ids_by_template(self::$PAGE_TEMPLATE);
            if (isset($pages[0]) && $page = get_post($pages[0])) {
              // Assuming we have at least one page with the page template
              $pagename[0] = $page->post_name;
              // then redirect to it the first service found.
              wp_safe_redirect('/'.implode('/',$pagename).'/');
              exit;
            }
          }
        } else if (count($pagename)>1) {
          // If there are child pages
          if (get_page_by_path($query_vars['pagename'])) {
            // If it is an actual child page then just let it pass thru
          } else {
              // If a virtual child page then see if parent has the template
            $parent = get_page_by_path($pagename[0]);
            $pages = self::get_page_ids_by_template(self::$PAGE_TEMPLATE);
            if (in_array($parent->ID,$pages)) {
              // If virtual child of a service page, change parent to the abstract root
              $pagename[0] = self::$ABSTRACT_ROOT;
              $query_vars['pagename'] = implode('/',$pagename);
              // The URL doesn't match a virtual page, will appropriately get a 404
            }
          }
        } else {
          $pagename = false;
        }
      }
      if (is_array($pagename))
        $query_vars['pagename'] = implode('/',$pagename);
      return $query_vars;
    }
    static function get_page_ids_by_template($template) {
      // This can be performance optimized but I wouldn't worry about it 
      // until there is a performance issue
      global $wpdb;
      $sql = "SELECT post_id FROM {$wpdb->postmeta} " . 
             "WHERE meta_key='_wp_page_template' AND meta_value='%s'";
      return $wpdb->get_col($wpdb->prepare($sql,$template));
    }
    static function page_link($link) {
      $parts = explode('/',$link);
      if ($parts[3]==self::$ABSTRACT_ROOT) {
        // This is really only useful for links in the admin.
        if (!is_admin()) {
          global $wp_query;
          if (!empty($wp_query->query_vars['pagename'])) {
            $pagename = explode('/',$wp_query->query_vars['pagename']);
          }
        } else {
          // If we get a URL that uses the abstract root, assign it the first service.
          $pages = self::get_page_ids_by_template(self::$PAGE_TEMPLATE);
          if (isset($pages[0]) && $page = get_post($pages[0])) {
            // Assuming we have at least one page with the page template
            $parts[3] = $page->post_name;
            // then redirect to it the first service found.
            $link = implode('/',$parts);
          } else {
            $link = preg_replace('#^(https?://[^/]+)(/.*)$#','$1',$link);
          }
        }
      }
      return $link;
    }
  }
  Yoursite_SharedChildPages::on_load();
}

And here are some screenshots that can illustrate the steps mentioned above:

Let me know if this is not what you were looking for and/or if you run into something that needs to be addressed by this solution in addition to what I have coded. Good luck!

7
  • This looks like it should be exactly what I need! I'll take a look in this couple of days, and get back to you. Thanks for all your help @MikeSchinkel. Commented Apr 3, 2011 at 7:48
  • @Ross Bearman - hope it works for you. Commented Apr 3, 2011 at 7:57
  • Thanks for the excellent advice, code and walkthrough @MikeSchinkel, this has been invaluable. Commented Apr 6, 2011 at 13:30
  • @Ross Bearman - Yay! Glad I could help. (I love answering these kind of questions!) Commented Apr 6, 2011 at 16:16
  • @MikeSchinkel Sorry to ask for more, but as explained in the original question, each service has it's own theme. This is currently handled somewhat hackily in the header using the following code: <?php elseif(is_page(site_service1_page()) || $post->post_parent == site_service1_page()): ?> <link href="<?php bloginfo('stylesheet_directory'); ?>/css/screen-service1.css" rel="stylesheet" type="text/css" /> This doesn't work with the base-services system, so is there a better way to get the root path (for example service1) in the header? Commented Apr 9, 2011 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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