July 15th, 2008

How to handle urls for “mini sites” in panels 2

This post is about how to handle urls for portals or mini sites or groups where there are multiple subpages in a given area of the site.

I ran into a slightly puzzling problem this week while working with the Civicactions team in Mexico. We’re doing a project for a client who has a large site which is heavily focused on Organic Groups. We’re using panels 2 for the entire layout, and of course pathauto is also in use.

Here is the gist:

We have four node types which are og_node types. The types are:

  • Campaigns
  • Toolkits
  • And a couple others…

So using path auto, if I have a campaign about water conservation, the path is:

campaigns/water_conserve -> node/123

node/% is overriden by panels, and using the panels arguments, we’ve got different layouts depending on if a campaign type node is passed in or a toolkit type node. (campaigns and toolkits have different sidebars, and some other stuff as well).

Okay, so that works fine, however each campaign has their own blog and the URL should be:

campaigns/water_conserve/blog

This should load another panel override where the URL is node/%/blog, but the above url DOES NOT map to node/123/blog, because the path module does not recourse back in the URL to find a match. With the blocks module (who would want to use this anymore?), this was okay using visibility settings, but it’s not too great to do in panels (even with mini panels as blocks).

So how do we resolve this?

I see three equally bad options:

  1. Write a hook into node_save and write a ton of url_alias entries like campaigns/water_conserve/blog, campaigns/water_conserve/resources, campaigns/water_conserve/people, etc. The problem with this is that it creates cruft, and it could leave artifacts, and it requires constant maintenance of these path aliases as new ones are added.
  2. We include logic everywhere (in all views, blocks, etc) to actually look at the URL and determine if it should load or not…. That’s pretty bad
  3. We build a RESTful structure using a slight hack in our custom client module This means that we find out that campaigns/water_conserve == “node/37″ and assume that campaigns/water_conserve/blog == “node/37/blog” (see below).


function mymod_menu($may_cache = false) {

if (!$may_cache) {
    // $args
    // for each arg
    // remove the arg and see if the path that is left is in path auto
    // example:
    // campaigns/mycampaign/blog/superblog
    // first loop:
    //   is campaigns/mycampaign/blog in the alias table (no).
    // second loop:
    //  is campaigns/mycampaign in the alias table (yes) -> node/123
    //  set the path

  $arguments = explode('/', $_GET['q']);

  // Only limit this to a couple of url paths to keep it safe?
  if (in_array($arguments[0],array('campaign','toolkit','group','organization'))) {
    while ($removed_args[] = array_pop($arguments) ) {
      $test = implode("/",$arguments);
      if ($real_path = drupal_get_normal_path($test)) {
        $real_path .= '/' . implode('/',$removed_args);
        menu_set_active_item($real_path);

        break;
      }
    }
  }

}

I don’t love this either, but it seems to work just fine. Does anyone have a problem with this? Should it be an option in path? Does it even matter in D6?

5 Responses to “How to handle urls for “mini sites” in panels 2”

  1. merlinofchaos says:

    You can accomplish the same basic thing in custom_url_rewrite(), which is something you also should put only in a client module.

  2. Jacob Singh says:

    Hi Earl,

    I looked into that, but it didn’t work for me. Furthermore, it conflicts with i18n which is a PITA… Maybe I did it wrong, but it really did not work for me… Perhaps I used the wrong code? Do you have an example?

    Thanks for posting!
    Jacob

  3. Jeff says:

    You may want to look at context and spaces. The context package includes the context_prefix module which handles generic path prefixing and also domain handing using the strategy Earl mentions above (and we’ve made it compatible with i18n). What context_prefix will do is establish a context based on the prefix, then strip the prefix off the path so the rest of Drupal respond like normal to a request. So for something like ‘water_campaigns/blog’ this would set a context value based on ‘water_campaigns’ and then let Drupal respond to ‘blog’. In the views that generate content at such a callback you’d add an argument to filter based on the context, so that only appropriate content would be shown. Spaces makes this second part a bit easier by providing OG integration. Among other things it’ll set an og_group_context based on a prefix, so that you only need to add the normal ‘is in the current group’ argument to you views and then everything ‘just works’.

  4. eric says:

    Hey Jacob,
    To follow up on Jeff’s note, here is a visualization of how context helps simplify site handling like blocks/menus/views/panels around it’s natural IA:
    http://www.developmentseed.org/blog/2008/jun/13/wri-org-redesign-launches-using-content-ui

    hope that helps the Mexican project. Te vayas bien!

  5. ekes says:

    I’ve just done similar on http://demo.nabuurtest.com/ http://feedback.nabuur.com/ (both devel sites one should be working to some degree at any moment) and did it with custom_url_rewrite.

    To solve the i18n problem I just called the i18n_path_alias from within my custom_url_rewrite. The i18n module is quite civilised here and doesn’t create a custom_url_rewrite if there is one existing already, so no patching required there either.

    So cutting useful bits from code you have something like:


    if ($op == 'alias') {
    $result = i18n_url_rewrite($op, $result, $path);
    ...
    elseif (preg_match("@node/([0-9]+)/($subpages_or)(?:/feed)?$@", $path, $path_pieces)) {
    // [group|village]/[background|images|...]
    $group_node = 'node' .'/'. $path_pieces[1];
    $group_path = drupal_lookup_path('alias', $group_node);
    if ($group_path != '') {
    $result = $group_path .'/'. $path_pieces[2];
    }
    }
    ...
    if ($op == 'source') {
    if (preg_match("@($types_or)/([^/\?\s]+)/($subpages_or)(?:/feed)?$@", $result, $path_pieces)) {
    // [group|village]/[background|images|...]
    $group_type = $path_pieces[1];
    $group_path = $group_type .'/'. $path_pieces[2];
    $group_node = drupal_lookup_path('source', $group_path);
    if ($group_node != '') {
    $result = $group_node .'/'. $path_pieces[3];
    )
    }
    ...
    $result = i18n_url_rewrite($op, $result, $path);
    )

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

How To find me

Telephone: +1 510.277.0891 | Email: jacobsingh at gmail daht calm

Solution Graphics