Adding keyed pair prefixing from the D6 branch.
authorJeff Miccolis
Tue, 9 Sep 2008 19:30:14 +0000 (19:30 +0000)
committerJeff Miccolis
Tue, 9 Sep 2008 19:30:14 +0000 (19:30 +0000)
context_prefix/context_prefix.module
context_prefix/context_prefix_admin.js [new file with mode: 0644]

index c8c8b51..6d97464 100644 (file)
@@ -4,6 +4,7 @@
 define('CONTEXT_PREFIX_PATH', 0);
 define('CONTEXT_PREFIX_SUBDOMAIN', 1);
 define('CONTEXT_PREFIX_DOMAIN', 2);
+define('CONTEXT_PREFIX_PAIR', 3);
 
 /**
  * Implementation of hook_menu().
@@ -50,6 +51,7 @@ function context_prefix_init() {
   if (!$once) {
     _context_prefix_init(CONTEXT_PREFIX_PATH);
     _context_prefix_init(CONTEXT_PREFIX_DOMAIN);
+    _context_prefix_init(CONTEXT_PREFIX_PAIR);
     $once = true;
   }
 }
@@ -62,19 +64,10 @@ function _context_prefix_init($method = CONTEXT_PREFIX_PATH) {
     case CONTEXT_PREFIX_PATH:
       $q = isset($_REQUEST["q"]) ? trim($_REQUEST["q"], "/") : '';
       $parsed = context_prefix_parse(CONTEXT_PREFIX_PATH, $q);
-      // if $_GET and $_REQUEST are different, the path has NOT been
-      // aliased. We may need to rewrite the path.
-      if ($_GET['q'] == $_REQUEST['q']) {
-        $q = context_prefix_unprefix($q);
-        // there is nothing beyond the path prefix -- treat as frontpage
-        if ($q == '') {
-          $_GET['q'] = variable_get('site_frontpage', 'node');
-        }
-        // pass the rest of the path onto Drupal cleanly
-        else {
-          $_REQUEST['q'] = $_GET['q'] = _context_prefix_get_normal_path($q);
-        }
-      }
+      break;
+    case CONTEXT_PREFIX_PAIR:
+      $q = isset($_REQUEST["q"]) ? trim($_REQUEST["q"], "/") : '';
+      $parsed = context_prefix_parse(CONTEXT_PREFIX_PAIR, $q);
       break;
     case CONTEXT_PREFIX_DOMAIN:
       $host = $_SERVER['HTTP_HOST'];
@@ -83,6 +76,21 @@ function _context_prefix_init($method = CONTEXT_PREFIX_PATH) {
       $parsed = context_prefix_parse(CONTEXT_PREFIX_DOMAIN, $q);
       break;
   }
+
+  // if $_GET and $_REQUEST are different, the path has NOT been
+  // aliased. We may need to rewrite the path.
+  if (in_array($method, array(CONTEXT_PREFIX_PATH, CONTEXT_PREFIX_PAIR)) && ($_GET['q'] == $_REQUEST['q'])) {
+    $q = context_prefix_unprefix($q, $method);
+    // there is nothing beyond the path prefix -- treat as frontpage
+    if ($q == '') {
+      $_GET['q'] = variable_get('site_frontpage', 'node');
+    }
+    // pass the rest of the path onto Drupal cleanly
+    else {
+      $_REQUEST['q'] = $_GET['q'] = _context_prefix_get_normal_path($q);
+    }
+  }
+
   if (is_array($parsed)) {
     foreach ($parsed as $prefix => $info) {
       context_prefix_set($method, $prefix, $info);
@@ -109,19 +117,24 @@ function context_prefix_url_rewrite($type, $path, $original) {
   // Check to see whether url rewriting has been disabled for this one
   // instance -- currently only possible through cl()
   if (!clswitch('get')) {
+    $active_path_prefixes = array();
+    
     // Retrieve the path prefixes for the current page that were
     // "stripped out" and write them back into url paths.
-    $active_path_prefixes = context_prefix_get(CONTEXT_PREFIX_PATH);
-    if ($active_path_prefixes) {
-      $parsed = context_prefix_parse(CONTEXT_PREFIX_PATH, $working_path);
+    foreach (context_prefix_get(CONTEXT_PREFIX_PAIR) as $item) {
+      $active_path_prefixes[] = $item['prefix'] .'/'. $item['id'];
+    }
+
+    foreach (context_prefix_get(CONTEXT_PREFIX_PATH) as $item) {
+      $active_path_prefixes[] = $item['prefix'];
+    }
+
+    if (count($active_path_prefixes)) {
+      $parsed = context_prefix_parse(CONTEXT_PREFIX_PATH, $working_path) + context_prefix_parse(CONTEXT_PREFIX_PAIR, $working_path);
       // A "normal" url was requested -- prefix the path
-      if ($type == 'alias' && !strpos($path, '://') && !count($parsed) && count($active_path_prefixes)) {
+      if (!$options['alias'] && !strpos($path, '://') && !count($parsed) && count($active_path_prefixes)) {
         $args = $args + $active_path_prefixes;
       }
-      // Source url was requested -- remove all prefixes
-      else if ($type == 'source' && count($parsed)) {
-        $working_path = context_prefix_unprefix($working_path);
-      }
     }
   }
 
@@ -134,8 +147,21 @@ function context_prefix_url_rewrite($type, $path, $original) {
 
 /**
  * Queries the database & modules for valid prefixes based on prefixing method.
+ *
+ * Modules that wish to provide in-code prefixes should implement the 
+ * hook_context_prefix_prefixes(). Which should return an array of prefixes by
+ * by provider. 
+ * 
+ * For example:
+ *
+ *  return array(
+ *    'my_module => array(
+ *      array('prefix' => 'foo', 'id' => 1),
+ *      array('prefix' => 'bar', 'id' => 2),
+ *    ),
+ *  );
  */
-function context_prefix_prefixes($method = CONTEXT_PREFIX_PATH) {
+function context_prefix_prefixes($requested_method = CONTEXT_PREFIX_PATH) {
   static $prefixes;
   if (!isset($prefixes)) {
     $prefixes = array();
@@ -143,18 +169,31 @@ function context_prefix_prefixes($method = CONTEXT_PREFIX_PATH) {
     // Invoke context_prefix_prefixes() and gather all prefixes
     // provided "in code" (or stored by their respective modules)
     foreach (module_invoke_all('context_prefix_prefixes') as $provider => $items) {
-      foreach ($items as $item) {
-        if ($item['prefix'] && $item['id']) {
-          $method = variable_get('context_prefix_method_'. $provider, CONTEXT_PREFIX_PATH);
-          $prefixes[$method][$item['prefix']] = array(
+      $method = variable_get('context_prefix_method_'. $provider, CONTEXT_PREFIX_PATH);
+
+      // If using a prefix pair we don't need to cache the valid prefixes.
+      if ($method == CONTEXT_PREFIX_PAIR) {
+        $prefix = variable_get('context_prefix_method_'. $provider .'_key', false);
+        if ($prefix != false) {
+          $prefixes[$method][$prefix] = array(
             'provider' => $provider,
-            'id' => $item['id'],
+            'id' => null,
           );
         }
       }
+      else {
+        foreach ($items as $item) {
+          if ($item['prefix'] && $item['id']) {
+            $prefixes[$method][$item['prefix']] = array(
+              'provider' => $provider,
+              'id' => $item['id'],
+            );
+          }
+        }
+      }
     }
 
-    // Gather all database prefixes
+    // Gather all database prefixes.
     $result = db_query("SELECT * FROM {context_prefix}");
     while ($row = db_fetch_object($result)) {
       $method = variable_get('context_prefix_method_'. $row->provider, CONTEXT_PREFIX_PATH);
@@ -164,7 +203,8 @@ function context_prefix_prefixes($method = CONTEXT_PREFIX_PATH) {
       );
     }
   }
-  return isset($prefixes[$method]) ? $prefixes[$method] : array();
+  
+  return (isset($prefixes[$requested_method]) ? $prefixes[$requested_method] : array());
 }
 
 /**
@@ -179,12 +219,17 @@ function context_prefix_parse($method = CONTEXT_PREFIX_PATH, $q) {
     // Parse the provided query string and provide an array of any prefixes found
     switch ($method) {
       case CONTEXT_PREFIX_PATH:
+      case CONTEXT_PREFIX_PAIR:      
         $parsed = array();
         $args = explode('/', $q);
         $arg = $args[0];
         while (isset($valid_prefixes[$arg])) {
           $parsed[$arg] = $valid_prefixes[$arg];
           array_shift($args);
+          if ($method == CONTEXT_PREFIX_PAIR) {
+            $parsed[$arg]['id'] = array_shift($args);
+          }
+          
           $arg = $args[0];
           if (in_array($arg, $parsed)) {
             break;
@@ -207,8 +252,8 @@ function context_prefix_parse($method = CONTEXT_PREFIX_PATH, $q) {
 /**
  * Removes any prefixes from a query string. For path prefixes only.
  */
-function context_prefix_unprefix($q, $providers = array()) {
-  $parsed = context_prefix_parse(CONTEXT_PREFIX_PATH, $q);
+function context_prefix_unprefix($q, $method, $providers = array()) {
+  $parsed = context_prefix_parse($method, $q);
   if (is_array($providers) && count($providers)) {
     foreach ($parsed as $prefix => $info) {
       if (!in_array($info['provider'], $providers)) {
@@ -218,12 +263,31 @@ function context_prefix_unprefix($q, $providers = array()) {
   }
   $parsed = array_keys($parsed);
   $args = explode('/', $q);
-  $args = array_diff($args, $parsed);
+  switch ($method) {
+    case CONTEXT_PREFIX_PATH:
+      $args = array_diff($args, $parsed);
+      break;
+    case CONTEXT_PREFIX_PAIR:
+      foreach ($parsed as $v) {
+        array_splice($args, array_search($v, $args), 2);
+      }
+      break;
+  }
   return implode('/', $args);
 }
 
 /**
  * Invokes hook_context_prefix_provider() to gather all providers.
+ *
+ * Modules that implement hook_context_prefix_provider need to return an
+ * array of prefix definitions. Each definition should have the following 
+ * keys:
+ *  - name
+ *  - description
+ *  - callback
+ *  - example 
+ *
+ * See the spaces module for an usage example.
  */
 function context_prefix_providers($by_method = FALSE) {
   static $providers;
@@ -233,17 +297,14 @@ function context_prefix_providers($by_method = FALSE) {
   }
   if ($by_method) {
     static $methods;
-    if (!is_array($methods)) {
-      $methods = array(
-        CONTEXT_PREFIX_PATH => array(),
-        CONTEXT_PREFIX_SUBDOMAIN => array(),
-        CONTEXT_PREFIX_DOMAIN => array(),
-      );
+    if (!isset($methods)) {
+      $methods = new context_prefix_cache();
+      
       foreach ($providers AS $id => $provider) {
-        $methods[variable_get('context_prefix_method_'. $id, CONTEXT_PREFIX_PATH)][$id] = $provider;
+        $methods->add(variable_get('context_prefix_method_'. $id, CONTEXT_PREFIX_PATH), array($id => $provider));
       }
     }
-    return $methods;
+    return $methods->get();
   }
   else {
     return $providers;
@@ -274,16 +335,13 @@ function _context_prefix_get_normal_path($path) {
 function _context_prefix_set($op = 'set', $type = CONTEXT_PREFIX_PATH, $prefix = '', $info = array()) {
   static $used;
   if (!$used) {
-    $used = array(
-      CONTEXT_PREFIX_PATH => array(),
-      CONTEXT_PREFIX_SUBDOMAIN => array(),
-      CONTEXT_PREFIX_DOMAIN => array(),
-    );
+    $used = new context_prefix_cache();
   }
   switch ($op) {
     case 'set':
       // Store prefix for url rewriting later on in the stack
-      $used[$type][] = $prefix;
+      $info['prefix'] = $prefix;
+      $used->add($type, $info, false);
 
       // Fire the provider callback
       if ($info['provider'] && $info['id']) {
@@ -297,10 +355,10 @@ function _context_prefix_set($op = 'set', $type = CONTEXT_PREFIX_PATH, $prefix =
       break;
     case 'get':
       if ($type === 'all') {
-        return $used;
+        return $used->get();
       }
       else {
-        return $used[$type];
+        return $used->get($type);
       }
   }
 }
@@ -335,10 +393,8 @@ function context_prefix_admin() {
   // Convert $page to an array, used by other functions.
   $pager_page_array = array($page);
 
-  $methods = array(
-    CONTEXT_PREFIX_PATH => t('Path prefix'),
-    CONTEXT_PREFIX_DOMAIN => t('Full domain'),
-  );
+  $methods = _context_ui_options();
+
   $merged = array();
   foreach(array_keys($methods) as $method) {
     foreach(context_prefix_prefixes($method) as $prefix => $info) {
@@ -378,12 +434,7 @@ function context_prefix_settings_form() {
   global $base_url;
   $form = array();
 
-  $options = array(
-    CONTEXT_PREFIX_PATH => t('Path prefix'),
-    CONTEXT_PREFIX_DOMAIN => t('Full domain'),
-    // TODO: Implement these features
-    // CONTEXT_PREFIX_SUBDOMAIN => t('Subdomain'),
-  );
+  $options = _context_ui_options();
 
   foreach (context_prefix_providers() as $id => $provider) {
     // Check to see whether provider has limited the available prefixing methods
@@ -396,14 +447,26 @@ function context_prefix_settings_form() {
     else {
       $provider_options = $options;
     }
-    $form['context_prefix_method_'. $id] = array(
+    
+    $form[$id] = array(
+      '#fieldset' => true,
       '#provider' => true,
       '#title' => $provider['name'],
       '#description' => $provider['description'],
+    );
+    $form[$id]['context_prefix_method_'. $id] = array(
+      '#title' => t('Method'),
       '#type' => 'select',
       '#options' => $provider_options,
       '#default_value' => variable_get('context_prefix_method_'. $id, CONTEXT_PREFIX_PATH),
     );
+    $form[$id]['context_prefix_method_'. $id .'_key'] = array(
+      '#title' => t('Key'),
+      '#type' => 'textfield',
+      '#size' => 12,
+      '#default_value' => variable_get('context_prefix_method_'. $id .'_key', ''),
+    );
+    
   }
 
   $form['context_prefix_location'] = array(
@@ -428,19 +491,24 @@ function theme_context_prefix_settings_form($form) {
   $output = '';
   $rows = array();
   foreach (element_children($form) as $id) {
-    if ($form[$id]['#provider']) {
+    $row = array();
+    if (isset($form[$id]['#provider'])) {
       $name = $form[$id]['#title'];
       $description = $form[$id]['#description'];
       unset($form[$id]['#title']);
       unset($form[$id]['#description']);
-      $rows[] = array(
-        "<strong>$name</strong><div class='description'>$description</div>",
-        drupal_render($form[$id]),
-      );
+      $row[] = "<strong>$name</strong><div class='description'>$description</div>";
+      
+      foreach (element_children($form[$id]) as $item) {
+        unset($form[$id][$item]['#title']);
+        $row[] = drupal_render($form[$id][$item]);
+      }
     }
+    $rows[] = $row;
   }
-  $output .= theme('table', array(t('Provider'), t('Prefix method')), $rows);
+  $output .= theme('table', array(t('Provider'), t('Prefix method'), t('Key')), $rows);
   $output .= drupal_render($form);
+  drupal_add_js(drupal_get_path("module", "context_prefix") ."/context_prefix_admin.js");
   return $output;
 }
 
@@ -530,19 +598,34 @@ function context_prefix_goto($provider, $prefix, $path = '', $query = NULL, $fra
    * context_prefix_goto('spaces', 'mygroup'), you should end up
    * at 'en/mygroup', not 'mygroup'
    */
-  switch (variable_get('context_prefix_method_'. $provider, CONTEXT_PREFIX_PATH)) {
-    case CONTEXT_PREFIX_PATH:
-      clswitch('set', true); // Drop prefixing for context_prefix goto's
-      drupal_goto($prefix .'/'. $path, $query, $fragment, $http_response_code);
-      break;
-    case CONTEXT_PREFIX_DOMAIN:
-      $prefixes = context_prefix_prefixes(CONTEXT_PREFIX_DOMAIN);
-      if (isset($prefixes[$prefix])) {
-        $path = 'http://'. $prefix .'/'. $path;
-        drupal_goto($path, $query, $fragment, $http_response_code);
-      }
-      break;
-  }
+  $method = variable_get('context_prefix_method_'. $provider, CONTEXT_PREFIX_PATH);
+  $prefixes = context_prefix_prefixes($method);
+
+  switch ($method) {
+   case CONTEXT_PREFIX_PATH:
+     foreach ($prefixes as $key => $info) {
+       if ($info['id'] == $id) {
+         clswitch('set', true); // Drop prefixing for context_prefix goto's
+         $path = $key .'/'. $path;
+         break;
+       }
+     }
+     break;
+   case CONTEXT_PREFIX_PAIR:
+     clswitch('set', true);
+     $prefixes = array_keys($prefixes);
+     $path = $prefixes[0] .'/'. $id .'/'. $path;
+     break;
+   case CONTEXT_PREFIX_DOMAIN:
+     foreach ($prefixes as $key => $info) {
+       if ($info['id'] == $id) {
+         $path = 'http://'. $key .'/'. $path;
+         break;
+       }
+     }
+     break;
+  }
+  drupal_goto($path, $query, $fragment, $http_response_code);
 }
 
 /**
@@ -722,3 +805,68 @@ function context_prefix_form_validate($form) {
     return true;
   }
 }
+
+/**
+* Specialized cache for storing prefix information.
+*/
+class context_prefix_cache {
+  
+  protected $cache = array();
+  
+  function __construct() {
+    $this->cache[CONTEXT_PREFIX_PATH] = array();
+    $this->cache[CONTEXT_PREFIX_PAIR] = array();
+    $this->cache[CONTEXT_PREFIX_SUBDOMAIN] = array();
+    $this->cache[CONTEXT_PREFIX_DOMAIN] = array();
+  }
+  
+  /**
+   * @param $method
+   *   The method to add to the cache for
+   * @param $item
+   *   Either a integer|string, or keyed array to add
+   * @param $merge
+   *   Preserve keys and merge into cache for method.
+   */
+  public function add($method, $item, $merge = true) {
+    if (is_array($item) && $merge) {
+      // Need to preserve keys so we use the '+' array operator.
+      $this->cache[$method] = $this->cache[$method] + $item;
+    }
+    else {
+      $this->cache[$method][] = $item;
+    }
+  }
+  
+  /**
+   * @param $method
+   *   The method to retrieve from the cache for.
+   * @param $item
+   *   Optionally and key of the required info.
+   *
+   * @return the desired info or false if an id doesn't exist.
+   */
+  public function get($method = false, $id = false) {
+    if ($method !== false && $id !== false) {
+      return (isset($this->cache[$method][$id]) ? $this->cache[$method][$id] : false);
+    }
+    elseif ($method !== false) {
+      return $this->cache[$method];
+    }
+    else {
+      return $this->cache;
+    }
+  }
+}
+
+/**
+ * Helper function, returns form options for prefix types.
+ */
+function _context_ui_options() {
+  return array(
+    CONTEXT_PREFIX_PATH => t('Path'),
+    CONTEXT_PREFIX_PAIR => t('Keyed pair'),
+    CONTEXT_PREFIX_DOMAIN => t('Full domain'),
+    // CONTEXT_PREFIX_SUBDOMAIN => t('Subdomain'),
+  );
+}
\ No newline at end of file
diff --git a/context_prefix/context_prefix_admin.js b/context_prefix/context_prefix_admin.js
new file mode 100644 (file)
index 0000000..b225209
--- /dev/null
@@ -0,0 +1,30 @@
+// $Id$
+
+if (typeof(Drupal) == "undefined" || !Drupal.context_prefix_admin) {
+  Drupal.context_prefix_admin = {};
+}
+
+Drupal.context_prefix_admin.attach = function() {
+  $('select[@id^="edit-context-prefix-"]').change(function(i){
+    Drupal.context_prefix_admin.alter(this);
+  }).each(function(i){
+    Drupal.context_prefix_admin.alter(this);
+  });
+}
+
+Drupal.context_prefix_admin.alter = function(elem){
+  if (elem.value === '3') {
+    $(elem).parents('tr').find('input[@id^="edit-context-prefix-"]').show();
+  }
+  else {
+    $(elem).parents('tr').find('input[@id^="edit-context-prefix-"]').hide();
+  }
+}
+
+if (Drupal.jsEnabled) {
+  $(document).ready(function() {
+    if ($('form#context-prefix-settings-form').size() > 0) {
+      Drupal.context_prefix_admin.attach();
+    }
+  });
+};
\ No newline at end of file