From 8d71033797c85b6837c164a01732ff3df104db40 Mon Sep 17 00:00:00 2001 From: Earl Miles <merlin@logrus.com> Date: Thu, 23 Apr 2009 23:58:55 +0000 Subject: [PATCH] Add the ability to automatically do .inc files for default objects like Views does as well as more powerful version controls. --- delegator/delegator.install | 12 +++ delegator/plugins/tasks/page.inc | 1 + includes/export.inc | 132 ++++++++++++++++++++----------- includes/plugins.inc | 74 ++++++++++++++--- 4 files changed, 159 insertions(+), 60 deletions(-) diff --git a/delegator/delegator.install b/delegator/delegator.install index e2619acb..d650bfca 100644 --- a/delegator/delegator.install +++ b/delegator/delegator.install @@ -24,6 +24,12 @@ function delegator_schema_1() { $schema['delegator_handlers'] = array( 'export' => array( 'identifier' => 'handler', + 'api' => array( + 'owner' => 'delegator', + 'api' => 'delegator_default', + 'minimum_version' => 1, + 'current_version' => 1, + ), ), 'fields' => array( 'did' => array( @@ -97,6 +103,12 @@ function delegator_schema_1() { 'description' => 'Contains page subtasks for implementing pages with arbitrary tasks.', 'export' => array( 'identifier' => 'page', + 'api' => array( + 'owner' => 'delegator', + 'api' => 'delegator_default', + 'minimum_version' => 1, + 'current_version' => 1, + ), ), 'fields' => array( 'pid' => array( diff --git a/delegator/plugins/tasks/page.inc b/delegator/plugins/tasks/page.inc index e89e9421..158d469e 100644 --- a/delegator/plugins/tasks/page.inc +++ b/delegator/plugins/tasks/page.inc @@ -377,6 +377,7 @@ function delegator_page_load($name) { */ function delegator_page_load_all($task = NULL) { ctools_include('export'); + if (empty($task)) { return ctools_export_load_object('delegator_pages'); } diff --git a/includes/export.inc b/includes/export.inc index 01c6c0c9..355825f2 100644 --- a/includes/export.inc +++ b/includes/export.inc @@ -47,8 +47,7 @@ define('EXPORT_IN_CODE', 0x02); */ function ctools_export_load_object($table, $type = 'all', $args = array()) { static $cache = array(); - static $cached_defaults = FALSE; - static $cached_database = FALSE; + static $cached_database = array(); $schema = ctools_export_get_schema($table); $export = $schema['export']; @@ -57,6 +56,11 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { $cache[$table] = array(); } + // If fetching all and cached all, we've done so and we are finished. + if ($type == 'all' && !empty($cached_database[$table])) { + return $cache[$table]; + } + $return = array(); // Don't load anything we've already cached. @@ -79,50 +83,42 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { $conditions = array(); $query_args = array(); - // We do not have to load anything if we have already cached everything. - if ($type != 'all' || !$cached_database) { - // If they passed in names, add them to the query. - if ($type == 'names') { - $conditions[] = "$export[key] IN (" . db_placeholders($args, $schema['fields'][$export['key']]['type']) . ")"; - $query_args = $args; - } - else if ($type == 'conditions') { - foreach ($args as $key => $value) { - if (isset($schema['fields'][$key])) { - $conditions[] = "$key = " . db_type_placeholder($schema['fields'][$key]['type']); - $query_args[] = $value; - } + // If they passed in names, add them to the query. + if ($type == 'names') { + $conditions[] = "$export[key] IN (" . db_placeholders($args, $schema['fields'][$export['key']]['type']) . ")"; + $query_args = $args; + } + else if ($type == 'conditions') { + foreach ($args as $key => $value) { + if (isset($schema['fields'][$key])) { + $conditions[] = "$key = " . db_type_placeholder($schema['fields'][$key]['type']); + $query_args[] = $value; } } + } - // Make a string out of the conditions. - if ($conditions) { - $query .= " WHERE " . implode(' AND ', $conditions); - } + // Make a string out of the conditions. + if ($conditions) { + $query .= " WHERE " . implode(' AND ', $conditions); + } - $result = db_query($query, $query_args); + $result = db_query($query, $query_args); - // Unpack the results of the query onto objects and cache them. - while ($data = db_fetch_object($result)) { - $object = _ctools_export_unpack_object($schema, $data, $export['object']); - $object->type = t('Normal'); - $object->export_type = EXPORT_IN_DATABASE; + // Unpack the results of the query onto objects and cache them. + while ($data = db_fetch_object($result)) { + $object = _ctools_export_unpack_object($schema, $data, $export['object']); + $object->type = t('Normal'); + $object->export_type = EXPORT_IN_DATABASE; - $cache[$table][$object->{$export['key']}] = $object; - if ($type == 'conditions') { - $return[$object->{$export['key']}] = $object; - } + $cache[$table][$object->{$export['key']}] = $object; + if ($type == 'conditions') { + $return[$object->{$export['key']}] = $object; } } - if ($type == 'all') { - $cached_database = TRUE; - } // @todo Load subrecords. - if ($export['default hook'] && !$cached_defaults) { - // @todo add a method to load .inc files for this. - $defaults = module_invoke_all($export['default hook']); + if ($defaults = _ctools_export_get_defaults($table, $export)) { $status = variable_get($export['status'], array()); foreach ($defaults as $object) { @@ -162,22 +158,12 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) { $return[$object->name] = $object; } } - - } - - // We only actually force this when retrieving all, because we may not - // have retrieved an object from the database and could thus incorrectly - // identify one as being a 'default' object when it is actually - // overridden. So we settle for a potential minor performance decrease - // in order to get this correct. - if ($type == 'all') { - // Make sure we don't run that again later on. - $cached_defaults = TRUE; } } // If fetching all, we've done so and we are finished. if ($type == 'all') { + $cached_database[$table] = TRUE; return $cache[$table]; } @@ -211,7 +197,7 @@ function ctools_get_default_object($table, $name) { } // @todo add a method to load .inc files for this. - $defaults = module_invoke_all($export['default hook']); + $defaults = _ctools_export_get_defaults($table, $export); $status = variable_get($export['status'], array()); if (!isset($defaults[$name])) { @@ -232,6 +218,53 @@ function ctools_get_default_object($table, $name) { return $object; } +/** + * Call the hook to get all default objects of the given type from the + * export. If configured properly, this could include loading up an API + * to get default objects. + */ +function _ctools_export_get_defaults($table, $export) { + static $cache = array(); + + if (!isset($cache[$table])) { + $cache[$table] = array(); + + if ($export['default hook']) { + if (!empty($export['api'])) { + $info = ctools_plugin_api_include($export['api']['owner'], $export['api']['api'], + $export['api']['minimum_version'], $export['api']['current_version']); + $modules = array_keys($info); + } + else { + $modules = module_implements($export['default hook']); + } + + foreach ($modules as $module) { + $function = $module . '_' . $export['default hook']; + if (function_exists($function)) { + if (empty($export['api'])) { + $cache[$table] += (array) $function($export); + } + else { + foreach ((array) $function($export) as $name => $object) { + // If version checking is enabled, ensure that the object can be used. + if (isset($object->api_version) && + $object->api_version >= $export['api']['minimum_version'] && + $object->api_version <= $export['api']['current_version']) { + $cache[$table][$name] = $object; + } + } + } + } + } + + drupal_alter($export['default hook'], $cache[$table]); + } + } + + return $cache[$table]; +} + /** * Unpack data loaded from the database onto an object. * @@ -321,7 +354,10 @@ function ctools_export_object($table, $object, $indent = '', $identifier = NULL, $output = $indent . '$' . $identifier . ' = new ' . get_class($object) . ";\n"; if ($schema['export']['can disable']) { - $output .= $indent . '$' . $identifier . '->disabled' . ' = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . "\n"; + $output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . "\n"; + } + if (!empty($schema['export']['api']['current_version'])) { + $output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ";\n"; } // Put top additions here: diff --git a/includes/plugins.inc b/includes/plugins.inc index 18a3d277..feb25743 100644 --- a/includes/plugins.inc +++ b/includes/plugins.inc @@ -11,16 +11,23 @@ */ /** - * Load a group of API files. + * Get an array of information about modules that support an API. * * This will ask each module if they support the given API, and if they do - * it will load the specified file name. The API and the file name - * coincide by design. + * it will return an array of information about the modules that do. * * This function invokes hook_ctools_api. This invokation is statically * cached, so feel free to call it as often per page run as you like, it * will cost very little. * + * This function can be used as an alternative to module_implements and can + * thus be used to find a precise list of modules that not only support + * a given hook (aka 'api') but also restrict to only modules that use + * the given version. This will allow multiple modules moving at different + * paces to still be able to work together and, in the event of a mismatch, + * either fall back to older behaviors or simply cease loading, which is + * still better than a crash. + * * @param $owner * The name of the module that controls the API. * @param $api @@ -36,9 +43,19 @@ * during operation. * * @return - * The API information, in case you need it. + * An array of API information, keyed by module. Each module's information will + * contain: + * - 'version': The version of the API required by the module. The module + * should use the lowest number it can support so that the widest range + * of supported versions can be used. + * - 'path': If not provided, this will be the module's path. This is + * where the module will store any subsidiary files. This differs from + * plugin paths which are figured separately. + * + * APIs can request any other information to be placed here that they might + * need. This should be in the documentation for that particular API. */ -function ctools_plugin_api_include($owner, $api, $minimum_version, $current_version) { +function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version) { static $cache = array(); if (!isset($cache[$owner][$api])) { $cache[$owner][$api] = array(); @@ -54,23 +71,56 @@ function ctools_plugin_api_include($owner, $api, $minimum_version, $current_vers if (!isset($info['path'])) { $info['path'] = drupal_get_path('module', $module); } - if (!isset($info['file'])) { - $info['file'] = "$module.$api.inc"; - } $cache[$owner][$api][$module] = $info; } } + } + + return $cache[$owner][$api]; +} + +/** + * Load a group of API files. + * + * This will ask each module if they support the given API, and if they do + * it will load the specified file name. The API and the file name + * coincide by design. + * + * @param $owner + * The name of the module that controls the API. + * @param $api + * The name of the api. The api name forms the file name: + * $module.$api.inc, though this can be overridden by the module's response. + * @param $minimum_version + * The lowest version API that is compatible with this one. If a module + * reports its API as older than this, its files will not be loaded. This + * should never change during operation. + * @param $current_version + * The current version of the api. If a module reports its minimum API as + * higher than this, its files will not be loaded. This should never change + * during operation. + * + * @return + * The API information, in case you need it. + */ +function ctools_plugin_api_include($owner, $api, $minimum_version, $current_version) { + static $already_done = array(); - // Now that we have a list, do our includes. - foreach ($cache[$owner][$api] as $module => $info) { + $info = ctools_plugin_api_info($owner, $api, $minimum_version, $current_version); + if (!isset($already_done[$owner][$api])) { + foreach ($info as $module => $info) { + if (!isset($info['file'])) { + $info['file'] = "$module.$api.inc"; + } if (file_exists("./$info[path]/$info[file]")) { - $cache[$owner][$api][$module]['included'] = TRUE; + $info[$module]['included'] = TRUE; require_once "./$info[path]/$info[file]"; } } + $already_done[$owner][$api] = TRUE; } - return $cache[$owner][$api]; + return $info; } /** -- GitLab