[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/ctools/includes/ -> plugins.inc (source)

   1  <?php
   2  // $Id: plugins.inc,v 1.18.2.30 2010/08/25 20:22:22 merlinofchaos Exp $
   3  
   4  /**
   5   * @file
   6   *
   7   * Contains routines to organize and load plugins. It allows a special
   8   * variation of the hook system so that plugins can be kept in separate
   9   * .inc files, and can be either loaded all at once or loaded only when
  10   * necessary.
  11   */
  12  
  13  /**
  14   * Get an array of information about modules that support an API.
  15   *
  16   * This will ask each module if they support the given API, and if they do
  17   * it will return an array of information about the modules that do.
  18   *
  19   * This function invokes hook_ctools_api. This invocation is statically
  20   * cached, so feel free to call it as often per page run as you like, it
  21   * will cost very little.
  22   *
  23   * This function can be used as an alternative to module_implements and can
  24   * thus be used to find a precise list of modules that not only support
  25   * a given hook (aka 'api') but also restrict to only modules that use
  26   * the given version. This will allow multiple modules moving at different
  27   * paces to still be able to work together and, in the event of a mismatch,
  28   * either fall back to older behaviors or simply cease loading, which is
  29   * still better than a crash.
  30   *
  31   * @param $owner
  32   *   The name of the module that controls the API.
  33   * @param $api
  34   *   The name of the api. The api name forms the file name:
  35   *   $module.$api.inc
  36   * @param $minimum_version
  37   *   The lowest version API that is compatible with this one. If a module
  38   *   reports its API as older than this, its files will not be loaded. This
  39   *   should never change during operation.
  40   * @param $current_version
  41   *   The current version of the api. If a module reports its minimum API as
  42   *   higher than this, its files will not be loaded. This should never change
  43   *   during operation.
  44   *
  45   * @return
  46   *   An array of API information, keyed by module. Each module's information will
  47   *   contain:
  48   *   - 'version': The version of the API required by the module. The module
  49   *     should use the lowest number it can support so that the widest range
  50   *     of supported versions can be used.
  51   *   - 'path': If not provided, this will be the module's path. This is
  52   *     where the module will store any subsidiary files. This differs from
  53   *     plugin paths which are figured separately.
  54   *
  55   *   APIs can request any other information to be placed here that they might
  56   *   need. This should be in the documentation for that particular API.
  57   */
  58  function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version) {
  59    $cache = &ctools_static(__FUNCTION__, array());
  60    if (!isset($cache[$owner][$api])) {
  61      $cache[$owner][$api] = array();
  62      foreach (module_implements('ctools_plugin_api') as $module) {
  63        $function = $module . '_ctools_plugin_api';
  64        $info = $function($owner, $api);
  65        if (!isset($info['version'])) {
  66          continue;
  67        }
  68  
  69        // Only process if version is between minimum and current, inclusive.
  70        if ($info['version'] >= $minimum_version && $info['version'] <= $current_version) {
  71          if (!isset($info['path'])) {
  72            $info['path'] = drupal_get_path('module', $module);
  73          }
  74          $cache[$owner][$api][$module] = $info;
  75        }
  76      }
  77  
  78      // And allow themes to implement these as well.
  79      $themes = _ctools_list_themes();
  80      foreach ($themes as $name => $theme) {
  81        if (!empty($theme->info['api'][$owner][$api])) {
  82          $info = $theme->info['api'][$owner][$api];
  83          if (!isset($info['version'])) {
  84            continue;
  85          }
  86  
  87          // Only process if version is between minimum and current, inclusive.
  88          if ($info['version'] >= $minimum_version && $info['version'] <= $current_version) {
  89            if (!isset($info['path'])) {
  90              $info['path'] = '';
  91            }
  92            // Because themes can't easily specify full path, we add it here
  93            // even though we do not for modules:
  94            $info['path'] = drupal_get_path('theme', $name) . '/' . $info['path'];
  95            $cache[$owner][$api][$name] = $info;
  96          }
  97        }
  98      }
  99    }
 100  
 101    return $cache[$owner][$api];
 102  }
 103  
 104  /**
 105   * Load a group of API files.
 106   *
 107   * This will ask each module if they support the given API, and if they do
 108   * it will load the specified file name. The API and the file name
 109   * coincide by design.
 110   *
 111   * @param $owner
 112   *   The name of the module that controls the API.
 113   * @param $api
 114   *   The name of the api. The api name forms the file name:
 115   *   $module.$api.inc, though this can be overridden by the module's response.
 116   * @param $minimum_version
 117   *   The lowest version API that is compatible with this one. If a module
 118   *   reports its API as older than this, its files will not be loaded. This
 119   *   should never change during operation.
 120   * @param $current_version
 121   *   The current version of the api. If a module reports its minimum API as
 122   *   higher than this, its files will not be loaded. This should never change
 123   *   during operation.
 124   *
 125   * @return
 126   *   The API information, in case you need it.
 127   */
 128  function ctools_plugin_api_include($owner, $api, $minimum_version, $current_version) {
 129    static $already_done = array();
 130  
 131    $info = ctools_plugin_api_info($owner, $api, $minimum_version, $current_version);
 132    if (!isset($already_done[$owner][$api])) {
 133      foreach ($info as $module => $plugin_info) {
 134        if (!isset($plugin_info['file'])) {
 135          $plugin_info['file'] = "$module.$api.inc";
 136        }
 137        if (file_exists("./$plugin_info[path]/$plugin_info[file]")) {
 138          $plugin_info[$module]['included'] = TRUE;
 139          require_once "./$plugin_info[path]/$plugin_info[file]";
 140        }
 141        $info[$module] = $plugin_info;
 142      }
 143      $already_done[$owner][$api] = TRUE;
 144    }
 145  
 146    return $info;
 147  }
 148  
 149  /**
 150   * Fetch a group of plugins by name.
 151   *
 152   * @param $module
 153   *   The name of the module that utilizes this plugin system. It will be
 154   *   used to call hook_ctools_plugin_$plugin() to get more data about the plugin.
 155   * @param $type
 156   *   The type identifier of the plugin.
 157   * @param $id
 158   *   If specified, return only information about plugin with this identifier.
 159   *   The system will do its utmost to load only plugins with this id.
 160   *
 161   * @return
 162   *   An array of information arrays about the plugins received. The contents
 163   *   of the array are specific to the plugin.
 164   */
 165  function ctools_get_plugins($module, $type, $id = NULL) {
 166    // Store local caches of plugins and plugin info so we don't have to do full
 167    // lookups everytime.
 168    $info = &ctools_static('ctools_plugin_info', array());
 169    $plugins = &ctools_static('ctools_plugins', array());
 170  
 171    // Attempt to shortcut this whole piece of code if we already have
 172    // the requested plugin:
 173    if ($id && isset($plugins[$module][$type]) && array_key_exists($id, $plugins[$module][$type])) {
 174      return $plugins[$module][$type][$id];
 175    }
 176  
 177    // Store the status of plugin loading. If a module plugin type pair is true,
 178    // then it is fully loaded and no searching or setup needs to be done.
 179    $setup = &ctools_static('ctools_plugin_setup', array());
 180  
 181    // Request metadata/defaults for this plugin from the declaring module. This
 182    // is done once per page request, upon a request being made for that plugin.
 183    if (!isset($info[$module][$type])) {
 184      $info[$module][$type] = ctools_plugin_get_info($module, $type);
 185      // Also, initialize the local plugin cache.
 186      $plugins[$module][$type] = array();
 187    }
 188  
 189    // We assume we don't need to build a cache.
 190    $build_cache = FALSE;
 191  
 192    // If the plugin info says this can be cached, check cache first.
 193    if ($info[$module][$type]['cache'] && empty($setup[$module][$type])) {
 194      $cache = cache_get("plugins:$module:$type", $info[$module][$type]['cache table']);
 195  
 196      if (!empty($cache->data)) {
 197        // Cache load succeeded so use the cached plugin list.
 198        $plugins[$module][$type]   = $cache->data;
 199        // Set $setup to true so we know things where loaded.
 200        $setup[$module][$type]     = TRUE;
 201      }
 202      else {
 203        // Cache load failed so store that we need to build and write the cache.
 204        $build_cache = TRUE;
 205      }
 206    }
 207  
 208    // Always load all hooks if we need them. Note we only need them now if the
 209    // plugin asks for them. We can assume that if we have plugins we've already
 210    // called the global hook.
 211    if (!empty($info[$module][$type]['use hooks']) && empty($plugins[$module][$type])) {
 212      $plugins[$module][$type] = ctools_plugin_load_hooks($info[$module][$type]);
 213    }
 214  
 215    // Then see if we should load all files. We only do this if we
 216    // want a list of all plugins or there was a cache miss.
 217    if (empty($setup[$module][$type]) && ($build_cache || !$id)) {
 218      $setup[$module][$type] = TRUE;
 219      $plugins[$module][$type] = array_merge($plugins[$module][$type], ctools_plugin_load_includes($info[$module][$type]));
 220      // If the plugin can have child plugins, and we're loading all plugins,
 221      // go through the list of plugins we have and find child plugins.
 222      if (!$id && !empty($info[$module][$type]['child plugins'])) {
 223        // If a plugin supports children, go through each plugin and ask.
 224        $temp = array();
 225        foreach ($plugins[$module][$type] as $name => $plugin) {
 226          if (!empty($plugin['get children']) && function_exists($plugin['get children'])) {
 227            $temp = array_merge($plugin['get children']($plugin, $name), $temp);
 228          }
 229          else {
 230            $temp[$name] = $plugin;
 231          }
 232        }
 233        $plugins[$module][$type] = $temp;
 234      }
 235    }
 236  
 237  
 238    // If we were told earlier that this is cacheable and the cache was
 239    // empty, give something back.
 240    if ($build_cache) {
 241      cache_set("plugins:$module:$type", $plugins[$module][$type], $info[$module][$type]['cache table']);
 242    }
 243  
 244    // If no id was requested, we are finished here:
 245    if (!$id) {
 246      // Use array_filter because looking for unknown plugins could cause NULL
 247      // entries to appear in the list later.
 248      return array_filter($plugins[$module][$type]);
 249    }
 250  
 251    // Check to see if we need to look for the file
 252    if (!array_key_exists($id, $plugins[$module][$type])) {
 253      // If we can have child plugins, check to see if the plugin name is in the
 254      // format of parent:child and break it up if it is.
 255      if (!empty($info[$module][$type]['child plugins']) && strpos($id, ':') !== FALSE) {
 256        list($parent, $child) = explode(':', $id, 2);
 257      }
 258      else {
 259        $parent = $id;
 260      }
 261  
 262      if (!array_key_exists($parent, $plugins[$module][$type])) {
 263        $result = ctools_plugin_load_includes($info[$module][$type], $parent);
 264        // Set to either what was returned or NULL.
 265        $plugins[$module][$type][$parent] = isset($result[$parent]) ? $result[$parent] : NULL;
 266      }
 267  
 268      // If we are looking for a child, and have the parent, ask the parent for the child.
 269      if (!empty($child) && !empty($plugins[$module][$type][$parent]) && function_exists($plugins[$module][$type][$parent]['get child'])) {
 270        $plugins[$module][$type][$id] = $plugins[$module][$type][$parent]['get child']($plugins[$module][$type][$parent], $parent, $child);
 271      }
 272    }
 273  
 274    // At this point we should either have the plugin, or a NULL.
 275    return $plugins[$module][$type][$id];
 276  }
 277  
 278  /**
 279   * Load plugins from a directory.
 280   *
 281   * @param $info
 282   *   The plugin info as returned by ctools_plugin_get_info()
 283   * @param $file
 284   *   The file to load if we're looking for just one particular plugin.
 285   *
 286   * @return
 287   *   An array of information created for this plugin.
 288   */
 289  function ctools_plugin_load_includes($info, $filename = NULL) {
 290    // Keep a static array so we don't hit drupal_system_listing more than necessary.
 291    $all_files = &ctools_static(__FUNCTION__, array());
 292  
 293    // If we're being asked for all plugins of a type, skip any caching
 294    // we may have done because this is an admin task and it's ok to
 295    // spend the extra time.
 296    if (!isset($filename)) {
 297      $all_files[$info['module']][$info['type']] = NULL;
 298    }
 299  
 300    if (!isset($all_files[$info['module']][$info['type']])) {
 301      // If a filename was set, we will try to load our list of files from
 302      // cache. This is considered normal operation and we try to reduce
 303      // the time spent finding files.
 304      if (isset($filename)) {
 305        $cache = cache_get("ctools_plugin_files:$info[module]:$info[type]");
 306        if ($cache) {
 307          $all_files[$info['module']][$info['type']] = $cache->data;
 308        }
 309      }
 310  
 311      if (!isset($all_files[$info['module']][$info['type']])) {
 312        $all_files[$info['module']][$info['type']] = array();
 313        // Load all our plugins.
 314        $directories = ctools_plugin_get_directories($info);
 315        $extension = empty($info['info file']) ? $info['extension'] : 'info';
 316  
 317        foreach ($directories as $module => $path) {
 318          $all_files[$info['module']][$info['type']][$module] = drupal_system_listing('\.' . $extension . '$', $path, 'name', 0);
 319        }
 320  
 321        cache_set("ctools_plugin_files:$info[module]:$info[type]", $all_files[$info['module']][$info['type']]);
 322      }
 323    }
 324    $file_list = $all_files[$info['module']][$info['type']];
 325    $plugins = array();
 326  
 327    // Iterate through all the plugin .inc files, load them and process the hook
 328    // that should now be available.
 329    foreach (array_filter($file_list) as $module => $files) {
 330      if ($filename) {
 331        $files = isset($files[$filename]) ? array($filename => $files[$filename]) : array();
 332      }
 333      foreach ($files as $file) {
 334        if (!empty($info['info file'])) {
 335          // Parse a .info file
 336          $result = ctools_plugin_process_info($info, $module, $file);
 337        }
 338        else {
 339          // Parse a hook.
 340          $plugin = NULL; // ensure that we don't have something leftover from earlier.
 341          include_once './' . $file->filename;
 342          // .inc files have a special format for the hook identifier.
 343          // For example, 'foo.inc' in the module 'mogul' using the plugin
 344          // whose hook is named 'borg_type' should have a function named (deep breath)
 345          // mogul_foo_borg_type()
 346  
 347          // If, however, the .inc file set the quasi-global $plugin array, we
 348          // can use that and not even call a function. Set the $identifier
 349          // appropriately and ctools_plugin_process() will handle it.
 350          $identifier = isset($plugin) ? $plugin : $module . '_' . $file->name;
 351          $result = ctools_plugin_process($info, $module, $identifier, dirname($file->filename), basename($file->filename), $file->name);
 352        }
 353        if (is_array($result)) {
 354          $plugins = array_merge($plugins, $result);
 355        }
 356      }
 357    }
 358    return $plugins;
 359  }
 360  
 361  /**
 362   * Get a list of directories to search for plugins of the given type.
 363   *
 364   * This utilizes hook_ctools_plugin_directory() to determine a complete list of
 365   * directories. Only modules that implement this hook and return a string
 366   * value will have their directories included.
 367   *
 368   * @param $info
 369   *   The $info array for the plugin as returned by ctools_plugin_get_info().
 370   *
 371   * @return array $directories
 372   *   An array of directories to search.
 373   */
 374  function ctools_plugin_get_directories($info) {
 375    $directories = array();
 376  
 377    foreach (module_implements('ctools_plugin_directory') as $module) {
 378      $function = $module . '_ctools_plugin_directory';
 379      $result = $function($info['module'], $info['type']);
 380      if ($result && is_string($result)) {
 381        $directories[$module] = drupal_get_path('module', $module) . '/' . $result;
 382      }
 383    }
 384  
 385    if (!empty($info['load themes'])) {
 386      $themes = _ctools_list_themes();
 387      foreach ($themes as $name => $theme) {
 388        if (!empty($theme->info['plugins'][$info['module']][$info['type']])) {
 389          $directories[$name] = drupal_get_path('theme', $name) . '/' . $theme->info['plugins'][$info['module']][$info['type']];
 390        }
 391      }
 392    }
 393    return $directories;
 394  }
 395  
 396  /**
 397   * Helper function to build a ctools-friendly list of themes capable of
 398   * providing plugins.
 399   *
 400   * @return array $themes
 401   *   A list of themes that can act as plugin providers, sorted parent-first with
 402   *   the active theme placed last.
 403   */
 404  function _ctools_list_themes() {
 405    static $themes;
 406    if (is_null($themes)) {
 407      $current = variable_get('theme_default', FALSE);
 408      $themes = $active = array();
 409      $all_themes = list_themes();
 410      foreach ($all_themes as $name => $theme) {
 411        // Only search from active themes
 412        if (empty($theme->status) && $theme->name != $current) {
 413          continue;
 414        }
 415        $active[$name] = $theme;
 416        // Prior to drupal 6.14, $theme->base_themes does not exist. Build it.
 417        if (!isset($theme->base_themes) && !empty($theme->base_theme)) {
 418          $active[$name]->base_themes = ctools_find_base_themes($all_themes, $name);
 419        }
 420      }
 421  
 422      // Construct a parent-first list of all themes
 423      foreach ($active as $name => $theme) {
 424        $base_themes = isset($theme->base_themes) ? $theme->base_themes : array();
 425        $themes = array_merge($themes, $base_themes, array($name => $theme->info['name']));
 426      }
 427      // Put the actual theme info objects into the array
 428      foreach (array_keys($themes) as $name) {
 429        $themes[$name] = $all_themes[$name];
 430      }
 431  
 432      // Make sure the current default theme always gets the last word
 433      if ($current_key = array_search($current, array_keys($themes))) {
 434        $themes += array_splice($themes, $current_key, 1);
 435      }
 436    }
 437    return $themes;
 438  }
 439  
 440  
 441  /**
 442   * Find all the base themes for the specified theme.
 443   *
 444   * Themes can inherit templates and function implementations from earlier themes.
 445   *
 446   * NOTE: this is a verbatim copy of system_find_base_themes(), which was not
 447   * implemented until 6.14. It is included here only as a fallback for outdated
 448   * versions of drupal core.
 449   *
 450   * @param $themes
 451   *   An array of available themes.
 452   * @param $key
 453   *   The name of the theme whose base we are looking for.
 454   * @param $used_keys
 455   *   A recursion parameter preventing endless loops.
 456   * @return
 457   *   Returns an array of all of the theme's ancestors; the first element's value
 458   *   will be NULL if an error occurred.
 459   */
 460  function ctools_find_base_themes($themes, $key, $used_keys = array()) {
 461    $base_key = $themes[$key]->info['base theme'];
 462    // Does the base theme exist?
 463    if (!isset($themes[$base_key])) {
 464      return array($base_key => NULL);
 465    }
 466  
 467    $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
 468  
 469    // Is the base theme itself a child of another theme?
 470    if (isset($themes[$base_key]->info['base theme'])) {
 471      // Do we already know the base themes of this theme?
 472      if (isset($themes[$base_key]->base_themes)) {
 473        return $themes[$base_key]->base_themes + $current_base_theme;
 474      }
 475      // Prevent loops.
 476      if (!empty($used_keys[$base_key])) {
 477        return array($base_key => NULL);
 478      }
 479      $used_keys[$base_key] = TRUE;
 480      return ctools_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
 481    }
 482    // If we get here, then this is our parent theme.
 483    return $current_base_theme;
 484  }
 485  
 486  
 487  /**
 488   * Load plugin info for the provided hook; this is handled separately from
 489   * plugins from files.
 490   *
 491   * @param $info
 492   *   The info array about the plugin as created by ctools_plugin_get_info()
 493   *
 494   * @return
 495   *   An array of info supplied by any hook implementations.
 496   */
 497  function ctools_plugin_load_hooks($info) {
 498    $hooks = array();
 499    foreach (module_implements($info['hook']) as $module) {
 500      $result = ctools_plugin_process($info, $module, $module, drupal_get_path('module', $module));
 501      if (is_array($result)) {
 502        $hooks = array_merge($hooks, $result);
 503      }
 504    }
 505    return $hooks;
 506  }
 507  
 508  /**
 509   * Process a single hook implementation of a ctools plugin.
 510   *
 511   * @param $info
 512   *   The $info array about the plugin as returned by ctools_plugin_get_info()
 513   * @param $module
 514   *   The module that implements the plugin being processed.
 515   * @param $identifier
 516   *   The plugin identifier, which is used to create the name of the hook
 517   *   function being called.
 518   * @param $path
 519   *   The path where files utilized by this plugin will be found.
 520   * @param $file
 521   *   The file that was loaded for this plugin, if it exists.
 522   * @param $base
 523   *   The base plugin name to use. If a file was loaded for the plugin, this
 524   *   is the plugin to assume must be present. This is used to automatically
 525   *   translate the array to make the syntax more friendly to plugin
 526   *   implementors.
 527   */
 528  function ctools_plugin_process($info, $module, $identifier, $path, $file = NULL, $base = NULL) {
 529    if (is_array($identifier)) {
 530      $result = $identifier;
 531    }
 532    else {
 533      $function = $identifier . '_' . $info['hook'];
 534      if (!function_exists($function)) {
 535        return NULL;
 536      }
 537      $result = $function();
 538      if (!isset($result) || !is_array($result)) {
 539        return NULL;
 540      }
 541    }
 542  
 543    // Automatically convert to the proper format that lets plugin implementations
 544    // not nest arrays as deeply as they used to, but still support the older
 545    // format where they do:
 546    if ($base && (!isset($result[$base]) || !is_array($result[$base]))) {
 547      $result = array($base => $result);
 548    }
 549  
 550    return _ctools_process_data($result, $info, $module, $path, $file);
 551  }
 552  
 553  /**
 554   * Fill in default values and run hooks for data loaded for one or
 555   * more plugins.
 556   */
 557  function _ctools_process_data($result, $info, $module, $path, $file) {
 558    // Fill in global defaults.
 559    foreach ($result as $name => $plugin) {
 560      $result[$name] += array(
 561        'module' => $module,
 562        'name' => $name,
 563        'path' => $path,
 564        'file' => $file,
 565        'plugin module' => $info['module'],
 566        'plugin type' => $info['type'],
 567      );
 568  
 569      // Fill in plugin-specific defaults, if they exist.
 570      if (!empty($info['defaults'])) {
 571        if (is_array($info['defaults'])) {
 572          $result[$name] += $info['defaults'];
 573        }
 574        // FIXME This callback-based approach for the 'defaults' key is entirely
 575        // redundant with the 'process' callback. Consequently, that approach is
 576        // DEPRECATED in favor using a 'process' callback. In the next major
 577        // version, 'defaults' callbacks will be removed entirely; only the array
 578        // addition 'defaults' approach will be allowed.
 579        else if (function_exists($info['defaults'])) {
 580          $info['defaults']($info, $result[$name]);
 581        }
 582      }
 583  
 584      // Allow the plugin owner to do additional processing.
 585      if (!empty($info['process']) && function_exists($info['process'])) {
 586        $info['process']($result[$name], $info);
 587      }
 588    }
 589    return $result;
 590  }
 591  
 592  
 593  /**
 594   * Process an info file for plugin information, rather than a hook.
 595   */
 596  function ctools_plugin_process_info($info, $module, $file) {
 597    $result = drupal_parse_info_file($file->filename);
 598    if ($result) {
 599      $result = array($file->name => $result);
 600      return _ctools_process_data($result, $info, $module, dirname($file->filename), basename($file->filename));
 601    }
 602  }
 603  
 604  /**
 605   * Ask a module for info about a particular plugin type.
 606   */
 607  function ctools_plugin_get_info($module, $type) {
 608    $info = array();
 609    $function = $module . '_ctools_plugin_' . $type;
 610    if (function_exists($function)) {
 611      $info = $function();
 612    }
 613  
 614    // Apply defaults. Array addition will not overwrite pre-existing keys.
 615    $info += array(
 616      'module' => $module,
 617      'type' => $type,
 618      'cache' => FALSE,
 619      'cache table' => 'cache',
 620      'use hooks' => TRUE, // TODO will default to FALSE in next major version
 621      'defaults' => array(),
 622      'process' => '',
 623      'extension' => 'inc',
 624      'info file' => FALSE,
 625      'hook' => $module . '_' . $type,
 626      'load themes' => FALSE,
 627    );
 628  
 629    return $info;
 630  }
 631  
 632  /**
 633   * Get a function from a plugin, if it exists. If the plugin is not already
 634   * loaded, try ctools_plugin_load_function() instead.
 635   *
 636   * @param $plugin_definition
 637   *   The loaded plugin type.
 638   * @param $function_name
 639   *   The identifier of the function. For example, 'settings form'.
 640   *
 641   * @return
 642   *   The actual name of the function to call, or NULL if the function
 643   *   does not exist.
 644   */
 645  function ctools_plugin_get_function($plugin_definition, $function_name) {
 646    // If cached the .inc file may not have been loaded. require_once is quite safe
 647    // and fast so it's okay to keep calling it.
 648    if (isset($plugin_definition['file'])) {
 649      // Plugins that are loaded from info files have the info file as
 650      // $plugin['file'].  Don't try to run those.
 651      $info = ctools_plugin_get_info($plugin_definition['plugin module'], $plugin_definition['plugin type']);
 652      if (empty($info['info file'])) {
 653        require_once './' . $plugin_definition['path'] . '/' . $plugin_definition['file'];
 654      }
 655    }
 656  
 657    if (!isset($plugin_definition[$function_name])) {
 658      return;
 659    }
 660  
 661    if (is_array($plugin_definition[$function_name]) && isset($plugin_definition[$function_name]['function'])) {
 662      $function = $plugin_definition[$function_name]['function'];
 663      if (isset($plugin_definition[$function_name]['file'])) {
 664        $file = $plugin_definition[$function_name]['file'];
 665        if (isset($plugin_definition[$function_name]['path'])) {
 666          $file = $plugin_definition[$function_name]['path'] . '/' . $file;
 667        }
 668        require_once './' . $file;
 669      }
 670    }
 671    else {
 672      $function = $plugin_definition[$function_name];
 673    }
 674  
 675    if (function_exists($function)) {
 676      return $function;
 677    }
 678  }
 679  
 680  /**
 681   * Load a plugin and get a function name from it, returning success only
 682   * if the function exists.
 683   *
 684   * @param $module
 685   *   The module that owns the plugin type.
 686   * @param $type
 687   *   The type of plugin.
 688   * @param $id
 689   *   The id of the specific plugin to load.
 690   * @param $function_name
 691   *   The identifier of the function. For example, 'settings form'.
 692   *
 693   * @return
 694   *   The actual name of the function to call, or NULL if the function
 695   *   does not exist.
 696   */
 697  function ctools_plugin_load_function($module, $type, $id, $function_name) {
 698    $plugin = ctools_get_plugins($module, $type, $id);
 699    return ctools_plugin_get_function($plugin, $function_name);
 700  }
 701  
 702  /**
 703   * Get a class from a plugin, if it exists. If the plugin is not already
 704   * loaded, try ctools_plugin_load_class() instead.
 705   *
 706   * @param $plugin_definition
 707   *   The loaded plugin type.
 708   * @param $class_name
 709   *   The identifier of the class. For example, 'handler'.
 710   * @param $abstract
 711   *   If true, will return abstract classes. Otherwise, parents will be included but nothing will be returned.
 712   *
 713   * @return
 714   *   The actual name of the class to call, or NULL if the class does not exist.
 715   */
 716  function ctools_plugin_get_class($plugin_definition, $class_name, $abstract = FALSE) {
 717    // If cached the .inc file may not have been loaded. require_once is quite safe
 718    // and fast so it's okay to keep calling it.
 719    if (isset($plugin_definition['file'])) {
 720      // Plugins that are loaded from info files have the info file as
 721      // $plugin['file'].  Don't try to run those.
 722      $info = ctools_plugin_get_info($plugin_definition['plugin module'], $plugin_definition['plugin type']);
 723      if (empty($info['info file'])) {
 724        require_once './' . $plugin_definition['path'] . '/' . $plugin_definition['file'];
 725      }
 726    }
 727  
 728    if (!isset($plugin_definition[$class_name])) {
 729      return;
 730    }
 731  
 732    if (is_array($plugin_definition[$class_name]) && isset($plugin_definition[$class_name]['class'])) {
 733      if (isset($plugin_definition[$class_name]['parent'])) {
 734        // Make sure parents are included.
 735        // TODO parent-loading needs to be better documented; the 'parent' designated
 736        // on the plugin actually corresponds not to the name of the parent CLASS,
 737        // but the name of the parent PLUGIN (and it then loads loads whatever
 738        // class is in the same $class_name slot). Initially unintuitive.
 739        ctools_plugin_load_class($plugin_definition['plugin module'], $plugin_definition['plugin type'], $plugin_definition[$class_name]['parent'], $class_name);
 740      }
 741      $class = $plugin_definition[$class_name]['class'];
 742      if (isset($plugin_definition[$class_name]['file'])) {
 743        $file = $plugin_definition[$class_name]['file'];
 744        if (isset($plugin_definition[$class_name]['path'])) {
 745          $file = $plugin_definition[$class_name]['path'] . '/' . $file;
 746        }
 747        require_once './' . $file;
 748      }
 749    }
 750    else {
 751      $class = $plugin_definition[$class_name];
 752    }
 753  
 754    // If we didn't explicitly include a file above, try autoloading a file
 755    // based on the class' name.
 756    if (!isset($file) && file_exists($plugin_definition['path'] . "/$class.class.php")) {
 757      require_once $plugin_definition['path'] . "/$class.class.php";
 758    }
 759  
 760    if (class_exists($class) &&
 761      (empty($plugin_definition[$class_name]['abstract']) || $abstract)) {
 762      return $class;
 763    }
 764  }
 765  
 766  /**
 767   * Load a plugin and get a class name from it, returning success only if the
 768   * class exists.
 769   *
 770   * @param $module
 771   *   The module that owns the plugin type.
 772   * @param $type
 773   *   The type of plugin.
 774   * @param $id
 775   *   The id of the specific plugin to load.
 776   * @param $class_name
 777   *   The identifier of the class. For example, 'handler'.
 778   * @param $abstract
 779   *   If true, will tell ctools_plugin_get_class to allow the return of abstract classes.
 780   *
 781   * @return
 782   *   The actual name of the class to call, or NULL if the class does not exist.
 783   */
 784  function ctools_plugin_load_class($module, $type, $id, $class_name, $abstract = FALSE) {
 785    $plugin = ctools_get_plugins($module, $type, $id);
 786    return ctools_plugin_get_class($plugin, $class_name, $abstract);
 787  }
 788  
 789  /**
 790   * Sort callback for sorting plugins naturally.
 791   *
 792   * Sort first by weight, then by title.
 793   */
 794  function ctools_plugin_sort($a, $b) {
 795    if (is_object($a)) {
 796      $a = (array) $a;
 797    }
 798    if (is_object($b)) {
 799      $b = (array) $b;
 800    }
 801  
 802    if (empty($a['weight'])) {
 803      $a['weight'] = 0;
 804    }
 805  
 806    if (empty($b['weight'])) {
 807      $b['weight'] = 0;
 808    }
 809  
 810    if ($a['weight'] == $b['weight']) {
 811      return strnatcmp(strtolower($a['title']), strtolower($b['title']));
 812    }
 813    return ($a['weight'] < $b['weight']) ? -1 : 1;
 814  }


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7