Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0 / 0 |
|
100.00% |
0 / 0 |
CRAP | |
41.96% |
240 / 572 |
|
| ctools_export_crud_new | |
0.00% |
0 / 1 |
0 | |
80.00% |
4 / 5 |
|||
| ctools_export_crud_load | |
0.00% |
0 / 1 |
0 | |
87.50% |
7 / 8 |
|||
| ctools_export_crud_load_multiple | |
0.00% |
0 / 1 |
0 | |
75.00% |
6 / 8 |
|||
| ctools_export_crud_load_all | |
0.00% |
0 / 1 |
0 | |
60.00% |
6 / 10 |
|||
| ctools_export_crud_save | |
0.00% |
0 / 1 |
0 | |
69.23% |
9 / 13 |
|||
| ctools_export_crud_delete | |
0.00% |
0 / 1 |
0 | |
88.89% |
8 / 9 |
|||
| ctools_export_crud_export | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 5 |
|||
| ctools_export_crud_import | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 17 |
|||
| ctools_export_crud_set_status | |
0.00% |
0 / 1 |
0 | |
70.00% |
7 / 10 |
|||
| ctools_export_crud_enable | |
100.00% |
1 / 1 |
0 | |
100.00% |
1 / 1 |
|||
| ctools_export_crud_disable | |
100.00% |
1 / 1 |
0 | |
100.00% |
1 / 1 |
|||
| ctools_export_load_object | |
0.00% |
0 / 1 |
0 | |
63.78% |
81 / 127 |
|||
| ctools_export_load_object_reset | |
0.00% |
0 / 1 |
0 | |
71.43% |
10 / 14 |
|||
| ctools_get_default_object | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 19 |
|||
| _ctools_export_get_defaults | |
0.00% |
0 / 1 |
0 | |
64.58% |
31 / 48 |
|||
| _ctools_export_get_defaults_from_cache | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 17 |
|||
| _ctools_export_get_some_defaults | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 11 |
|||
| _ctools_export_unpack_object | |
0.00% |
0 / 1 |
0 | |
50.00% |
12 / 24 |
|||
| ctools_export_unpack_object | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 2 |
|||
| ctools_var_export | |
0.00% |
0 / 1 |
0 | |
5.56% |
1 / 18 |
|||
| ctools_export_object | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 51 |
|||
| ctools_export_get_schema | |
0.00% |
0 / 1 |
0 | |
80.56% |
29 / 36 |
|||
| ctools_export_get_schemas | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 12 |
|||
| _ctools_export_filter_export_tables | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 1 |
|||
| ctools_export_get_schemas_by_module | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 6 |
|||
| ctools_export_set_status | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 5 |
|||
| ctools_export_set_object_status | |
100.00% |
1 / 1 |
0 | |
100.00% |
10 / 10 |
|||
| ctools_export_form | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 7 |
|||
| ctools_export_new_object | |
0.00% |
0 / 1 |
0 | |
88.24% |
15 / 17 |
|||
| ctools_export_to_hook_code | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 21 |
|||
| ctools_export_default_to_hook_code | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 17 |
|||
| ctools_export_default_list | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 20 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains code to make it easier to have exportable objects. | |
| * | |
| * Documentation for exportable objects is contained in help/export.html | |
| */ | |
| /** | |
| * A bit flag used to let us know if an object is in the database. | |
| */ | |
| define('EXPORT_IN_DATABASE', 0x01); | |
| /** | |
| * A bit flag used to let us know if an object is a 'default' in code. | |
| */ | |
| define('EXPORT_IN_CODE', 0x02); | |
| /** | |
| * @defgroup export_crud CRUD functions for export. | |
| * @{ | |
| * export.inc supports a small number of CRUD functions that should always | |
| * work for every exportable object, no matter how complicated. These | |
| * functions allow complex objects to provide their own callbacks, but | |
| * in most cases, the default callbacks will be used. | |
| * | |
| * Note that defaults are NOT set in the $schema because it is presumed | |
| * that a module's personalized CRUD functions will already know which | |
| * $table to use and not want to clutter up the arguments with it. | |
| */ | |
| /** | |
| * Create a new object for the given $table. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $set_defaults | |
| * If TRUE, which is the default, then default values will be retrieved | |
| * from schema fields and set on the object. | |
| * | |
| * @return | |
| * The loaded object. | |
| */ | |
| function ctools_export_crud_new($table, $set_defaults = TRUE) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['create callback']) && function_exists($export['create callback'])) { | |
| return $export['create callback']($set_defaults); | |
| } | |
| else { | |
| return ctools_export_new_object($table, $set_defaults); | |
| } | |
| } | |
| /** | |
| * Load a single exportable object. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $name | |
| * The unique ID to load. The field for this ID will be specified by | |
| * the export key, which normally defaults to 'name'. | |
| * | |
| * @return | |
| * The loaded object. | |
| */ | |
| function ctools_export_crud_load($table, $name) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['load callback']) && function_exists($export['load callback'])) { | |
| return $export['load callback']($name); | |
| } | |
| else { | |
| $result = ctools_export_load_object($table, 'names', array($name)); | |
| if (isset($result[$name])) { | |
| return $result[$name]; | |
| } | |
| } | |
| } | |
| /** | |
| * Load multiple exportable objects. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $names | |
| * An array of unique IDs to load. The field for these IDs will be specified | |
| * by the export key, which normally defaults to 'name'. | |
| * | |
| * @return | |
| * An array of the loaded objects. | |
| */ | |
| function ctools_export_crud_load_multiple($table, array $names) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| $results = array(); | |
| if (!empty($export['load multiple callback']) && function_exists($export['load multiple callback'])) { | |
| $results = $export['load multiple callback']($names); | |
| } | |
| else { | |
| $results = ctools_export_load_object($table, 'names', $names); | |
| } | |
| // Ensure no empty results are returned. | |
| return array_filter($results); | |
| } | |
| /** | |
| * Load all exportable objects of a given type. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $reset | |
| * If TRUE, the static cache of all objects will be flushed prior to | |
| * loading all. This can be important on listing pages where items | |
| * might have changed on the page load. | |
| * @return | |
| * An array of all loaded objects, keyed by the unique IDs of the export key. | |
| */ | |
| function ctools_export_crud_load_all($table, $reset = FALSE) { | |
| $schema = ctools_export_get_schema($table); | |
| if (empty($schema['export'])) { | |
| return array(); | |
| } | |
| $export = $schema['export']; | |
| if ($reset) { | |
| ctools_export_load_object_reset($table); | |
| } | |
| if (!empty($export['load all callback']) && function_exists($export['load all callback'])) { | |
| return $export['load all callback']($reset); | |
| } | |
| else { | |
| return ctools_export_load_object($table, 'all'); | |
| } | |
| } | |
| /** | |
| * Save a single exportable object. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $object | |
| * The fully populated object to save. | |
| * | |
| * @return | |
| * Failure to write a record will return FALSE. Otherwise SAVED_NEW or | |
| * SAVED_UPDATED is returned depending on the operation performed. The | |
| * $object parameter contains values for any serial fields defined by the $table | |
| */ | |
| function ctools_export_crud_save($table, &$object) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['save callback']) && function_exists($export['save callback'])) { | |
| return $export['save callback']($object); | |
| } | |
| else { | |
| // Objects should have a serial primary key. If not, simply fail to write. | |
| if (empty($export['primary key'])) { | |
| return FALSE; | |
| } | |
| $key = $export['primary key']; | |
| if ($object->export_type & EXPORT_IN_DATABASE) { | |
| // Existing record. | |
| $update = array($key); | |
| } | |
| else { | |
| // New record. | |
| $update = array(); | |
| $object->export_type = EXPORT_IN_DATABASE; | |
| } | |
| return drupal_write_record($table, $object, $update); | |
| } | |
| } | |
| /** | |
| * Delete a single exportable object. | |
| * | |
| * This only deletes from the database, which means that if an item is in | |
| * code, then this is actually a revert. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $object | |
| * The fully populated object to delete, or the export key. | |
| */ | |
| function ctools_export_crud_delete($table, $object) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['delete callback']) && function_exists($export['delete callback'])) { | |
| return $export['delete callback']($object); | |
| } | |
| else { | |
| // If we were sent an object, get the export key from it. Otherwise | |
| // assume we were sent the export key. | |
| $value = is_object($object) ? $object->{$export['key']} : $object; | |
| db_delete($table) | |
| ->condition($export['key'], $value) | |
| ->execute(); | |
| } | |
| } | |
| /** | |
| * Get the exported code of a single exportable object. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $object | |
| * The fully populated object to delete, or the export key. | |
| * @param $indent | |
| * Any indentation to apply to the code, in case this object is embedded | |
| * into another, for example. | |
| * | |
| * @return | |
| * A string containing the executable export of the object. | |
| */ | |
| function ctools_export_crud_export($table, $object, $indent = '') { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['export callback']) && function_exists($export['export callback'])) { | |
| return $export['export callback']($object, $indent); | |
| } | |
| else { | |
| return ctools_export_object($table, $object, $indent); | |
| } | |
| } | |
| /** | |
| * Turn exported code into an object. | |
| * | |
| * Note: If the code is poorly formed, this could crash and there is no | |
| * way to prevent this. | |
| * | |
| * @param $table | |
| * The name of the table to use to retrieve $schema values. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $code | |
| * The code to eval to create the object. | |
| * | |
| * @return | |
| * An object created from the export. This object will NOT have been saved | |
| * to the database. In the case of failure, a string containing all errors | |
| * that the system was able to determine. | |
| */ | |
| function ctools_export_crud_import($table, $code) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['import callback']) && function_exists($export['import callback'])) { | |
| return $export['import callback']($code); | |
| } | |
| else { | |
| ob_start(); | |
| eval($code); | |
| ob_end_clean(); | |
| if (empty(${$export['identifier']})) { | |
| $errors = ob_get_contents(); | |
| if (empty($errors)) { | |
| $errors = t('No item found.'); | |
| } | |
| return $errors; | |
| } | |
| $item = ${$export['identifier']}; | |
| // Set these defaults just the same way that ctools_export_new_object sets | |
| // them. | |
| $item->export_type = NULL; | |
| $item->{$export['export type string']} = t('Local'); | |
| return $item; | |
| } | |
| } | |
| /** | |
| * Change the status of a certain object. | |
| * | |
| * @param $table | |
| * The name of the table to use to enable a certain object. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $object | |
| * The fully populated object to enable, or the machine readable name. | |
| * @param $status | |
| * The status, in this case, is whether or not it is 'disabled'. | |
| */ | |
| function ctools_export_crud_set_status($table, $object, $status) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!empty($export['status callback']) && function_exists($export['status callback'])) { | |
| $export['status callback']($object, $status); | |
| } | |
| else { | |
| if (is_object($object)) { | |
| ctools_export_set_object_status($object, $status); | |
| } | |
| else { | |
| ctools_export_set_status($table, $object, $status); | |
| } | |
| } | |
| } | |
| /** | |
| * Enable a certain object. | |
| * | |
| * @param $table | |
| * The name of the table to use to enable a certain object. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $object | |
| * The fully populated object to enable, or the machine readable name. | |
| */ | |
| function ctools_export_crud_enable($table, $object) { | |
| return ctools_export_crud_set_status($table, $object, FALSE); | |
| } | |
| /** | |
| * Disable a certain object. | |
| * | |
| * @param $table | |
| * The name of the table to use to disable a certain object. This table | |
| * must have an 'export' section containing data or this function | |
| * will fail. | |
| * @param $object | |
| * The fully populated object to disable, or the machine readable name. | |
| */ | |
| function ctools_export_crud_disable($table, $object) { | |
| return ctools_export_crud_set_status($table, $object, TRUE); | |
| } | |
| /** | |
| * @} | |
| */ | |
| /** | |
| * Load some number of exportable objects. | |
| * | |
| * This function will cache the objects, load subsidiary objects if necessary, | |
| * check default objects in code and properly set them up. It will cache | |
| * the results so that multiple calls to load the same objects | |
| * will not cause problems. | |
| * | |
| * It attempts to reduce, as much as possible, the number of queries | |
| * involved. | |
| * | |
| * @param $table | |
| * The name of the table to be loaded from. Data is expected to be in the | |
| * schema to make all this work. | |
| * @param $type | |
| * A string to notify the loader what the argument is | |
| * - all: load all items. This is the default. $args is unused. | |
| * - names: $args will be an array of specific named objects to load. | |
| * - conditions: $args will be a keyed array of conditions. The conditions | |
| * must be in the schema for this table or errors will result. | |
| * @param $args | |
| * An array of arguments whose actual use is defined by the $type argument. | |
| */ | |
| function ctools_export_load_object($table, $type = 'all', $args = array()) { | |
| $cache = &drupal_static(__FUNCTION__); | |
| $cache_table_exists = &drupal_static(__FUNCTION__ . '_table_exists', array()); | |
| $cached_database = &drupal_static('ctools_export_load_object_all'); | |
| if (!array_key_exists($table, $cache_table_exists)) { | |
| $cache_table_exists[$table] = db_table_exists($table); | |
| } | |
| $schema = ctools_export_get_schema($table); | |
| if (empty($schema) || !$cache_table_exists[$table]) { | |
| return array(); | |
| } | |
| $export = $schema['export']; | |
| if (!isset($cache[$table])) { | |
| $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. | |
| if ($type == 'names' && !empty($args)) { | |
| foreach ($args as $id => $name) { | |
| if (isset($cache[$table][$name])) { | |
| $return[$name] = $cache[$table][$name]; | |
| unset($args[$id]); | |
| } | |
| } | |
| // If nothing left to load, return the result. | |
| if (empty($args)) { | |
| return $return; | |
| } | |
| } | |
| // Build the query | |
| $query = db_select($table, 't__0')->fields('t__0'); | |
| $alias_count = 1; | |
| if (!empty($schema['join'])) { | |
| foreach ($schema['join'] as $join_key => $join) { | |
| if ($join_schema = drupal_get_schema($join['table'])) { | |
| $query->join($join['table'], 't__' . $alias_count, 't__0.' . $join['left_key'] . ' = ' . 't__' . $alias_count . '.' . $join['right_key']); | |
| $query->fields('t__' . $alias_count); | |
| $alias_count++; | |
| // Allow joining tables to alter the query through a callback. | |
| if (isset($join['callback']) && function_exists($join['callback'])) { | |
| $join['callback']($query, $schema, $join_schema); | |
| } | |
| } | |
| } | |
| } | |
| $conditions = array(); | |
| $query_args = array(); | |
| // If they passed in names, add them to the query. | |
| if ($type == 'names') { | |
| $query->condition($export['key'], $args, 'IN'); | |
| } | |
| else if ($type == 'conditions') { | |
| foreach ($args as $key => $value) { | |
| if (isset($schema['fields'][$key])) { | |
| $query->condition($key, $value); | |
| } | |
| } | |
| } | |
| $result = $query->execute(); | |
| $status = variable_get($export['status'], array()); | |
| // Unpack the results of the query onto objects and cache them. | |
| foreach ($result as $data) { | |
| if (isset($schema['export']['object factory']) && function_exists($schema['export']['object factory'])) { | |
| $object = $schema['export']['object factory']($schema, $data); | |
| } | |
| else { | |
| $object = _ctools_export_unpack_object($schema, $data, $export['object']); | |
| } | |
| $object->table = $table; | |
| $object->{$export['export type string']} = t('Normal'); | |
| $object->export_type = EXPORT_IN_DATABASE; | |
| // Determine if default object is enabled or disabled. | |
| if (isset($status[$object->{$export['key']}])) { | |
| $object->disabled = $status[$object->{$export['key']}]; | |
| } | |
| $cache[$table][$object->{$export['key']}] = $object; | |
| if ($type == 'conditions') { | |
| $return[$object->{$export['key']}] = $object; | |
| } | |
| } | |
| // Load subrecords. | |
| if (isset($export['subrecords callback']) && function_exists($export['subrecords callback'])) { | |
| $export['subrecords callback']($cache[$table]); | |
| } | |
| if ($type == 'names' && !empty($args) && !empty($export['cache defaults'])) { | |
| $defaults = _ctools_export_get_some_defaults($table, $export, $args); | |
| } | |
| else { | |
| $defaults = _ctools_export_get_defaults($table, $export); | |
| } | |
| if ($defaults) { | |
| foreach ($defaults as $object) { | |
| if ($type == 'conditions') { | |
| // if this does not match all of our conditions, skip it. | |
| foreach ($args as $key => $value) { | |
| if (!isset($object->$key)) { | |
| continue 2; | |
| } | |
| if (is_array($value)) { | |
| if (!in_array($object->$key, $value)) { | |
| continue 2; | |
| } | |
| } | |
| else if ($object->$key != $value) { | |
| continue 2; | |
| } | |
| } | |
| } | |
| else if ($type == 'names') { | |
| if (!in_array($object->{$export['key']}, $args)) { | |
| continue; | |
| } | |
| } | |
| // Determine if default object is enabled or disabled. | |
| if (isset($status[$object->{$export['key']}])) { | |
| $object->disabled = $status[$object->{$export['key']}]; | |
| } | |
| if (!empty($cache[$table][$object->{$export['key']}])) { | |
| $cache[$table][$object->{$export['key']}]->{$export['export type string']} = t('Overridden'); | |
| $cache[$table][$object->{$export['key']}]->export_type |= EXPORT_IN_CODE; | |
| $cache[$table][$object->{$export['key']}]->export_module = isset($object->export_module) ? $object->export_module : NULL; | |
| if ($type == 'conditions') { | |
| $return[$object->{$export['key']}] = $cache[$table][$object->{$export['key']}]; | |
| } | |
| } | |
| else { | |
| $object->{$export['export type string']} = t('Default'); | |
| $object->export_type = EXPORT_IN_CODE; | |
| $object->in_code_only = TRUE; | |
| $object->table = $table; | |
| $cache[$table][$object->{$export['key']}] = $object; | |
| if ($type == 'conditions') { | |
| $return[$object->{$export['key']}] = $object; | |
| } | |
| } | |
| } | |
| } | |
| // If fetching all, we've done so and we are finished. | |
| if ($type == 'all') { | |
| $cached_database[$table] = TRUE; | |
| return $cache[$table]; | |
| } | |
| if ($type == 'names') { | |
| foreach ($args as $name) { | |
| if (isset($cache[$table][$name])) { | |
| $return[$name] = $cache[$table][$name]; | |
| } | |
| } | |
| } | |
| // For conditions, | |
| return $return; | |
| } | |
| /** | |
| * Reset all static caches in ctools_export_load_object() or static caches for | |
| * a given table in ctools_export_load_object(). | |
| * | |
| * @param $table | |
| * String that is the name of a table. If not defined, all static caches in | |
| * ctools_export_load_object() will be reset. | |
| */ | |
| function ctools_export_load_object_reset($table = NULL) { | |
| // Reset plugin cache to make sure new include files are picked up. | |
| ctools_include('plugins'); | |
| ctools_get_plugins_reset(); | |
| if (empty($table)) { | |
| drupal_static_reset('ctools_export_load_object'); | |
| drupal_static_reset('ctools_export_load_object_all'); | |
| drupal_static_reset('_ctools_export_get_defaults'); | |
| } | |
| else { | |
| $cache = &drupal_static('ctools_export_load_object'); | |
| $cached_database = &drupal_static('ctools_export_load_object_all'); | |
| $cached_defaults = &drupal_static('_ctools_export_get_defaults'); | |
| unset($cache[$table]); | |
| unset($cached_database[$table]); | |
| unset($cached_defaults[$table]); | |
| } | |
| } | |
| /** | |
| * Get the default version of an object, if it exists. | |
| * | |
| * This function doesn't care if an object is in the database or not and | |
| * does not check. This means that export_type could appear to be incorrect, | |
| * because a version could exist in the database. However, it's not | |
| * incorrect for this function as it is *only* used for the default | |
| * in code version. | |
| */ | |
| function ctools_get_default_object($table, $name) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| if (!$export['default hook']) { | |
| return; | |
| } | |
| // Try to load individually from cache if this cache is enabled. | |
| if (!empty($export['cache defaults'])) { | |
| $defaults = _ctools_export_get_some_defaults($table, $export, array($name)); | |
| } | |
| else { | |
| $defaults = _ctools_export_get_defaults($table, $export); | |
| } | |
| $status = variable_get($export['status'], array()); | |
| if (!isset($defaults[$name])) { | |
| return; | |
| } | |
| $object = $defaults[$name]; | |
| // Determine if default object is enabled or disabled. | |
| if (isset($status[$object->{$export['key']}])) { | |
| $object->disabled = $status[$object->{$export['key']}]; | |
| } | |
| $object->{$export['export type string']} = t('Default'); | |
| $object->export_type = EXPORT_IN_CODE; | |
| $object->in_code_only = TRUE; | |
| 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) { | |
| $cache = &drupal_static(__FUNCTION__, array()); | |
| // If defaults may be cached, first see if we can load from cache. | |
| if (!isset($cache[$table]) && !empty($export['cache defaults'])) { | |
| $cache[$table] = _ctools_export_get_defaults_from_cache($table, $export); | |
| } | |
| if (!isset($cache[$table])) { | |
| // If we're caching, attempt to get a lock. We will wait a short time | |
| // on the lock, but not too long, because it's better to just rebuild | |
| // and throw away results than wait too long on a lock. | |
| if (!empty($export['cache defaults'])) { | |
| for ($counter = 0; !($lock = lock_acquire('ctools_export:' . $table)) && $counter > 5; $counter++) { | |
| lock_wait('ctools_export:' . $table, 1); | |
| ++$counter; | |
| } | |
| } | |
| $cache[$table] = array(); | |
| if ($export['default hook']) { | |
| if (!empty($export['api'])) { | |
| ctools_include('plugins'); | |
| $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)) { | |
| foreach ((array) $function($export) as $name => $object) { | |
| // Record the module that provides this exportable. | |
| $object->export_module = $module; | |
| if (empty($export['api'])) { | |
| $cache[$table][$name] = $object; | |
| } | |
| else { | |
| // If version checking is enabled, ensure that the object can be used. | |
| if (isset($object->api_version) && | |
| version_compare($object->api_version, $export['api']['minimum_version']) >= 0 && | |
| version_compare($object->api_version, $export['api']['current_version']) <= 0) { | |
| $cache[$table][$name] = $object; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| drupal_alter($export['default hook'], $cache[$table]); | |
| // If we acquired a lock earlier, cache the results and release the | |
| // lock. | |
| if (!empty($lock)) { | |
| // Cache the index. | |
| $index = array_keys($cache[$table]); | |
| cache_set('ctools_export_index:' . $table, $index, $export['default cache bin']); | |
| // Cache each object. | |
| foreach ($cache[$table] as $name => $object) { | |
| cache_set('ctools_export:' . $table . ':' . $name, $object, $export['default cache bin']); | |
| } | |
| lock_release('ctools_export:' . $table); | |
| } | |
| } | |
| } | |
| return $cache[$table]; | |
| } | |
| /** | |
| * Attempt to load default objects from cache. | |
| * | |
| * We can be instructed to cache default objects by the schema. If so | |
| * we cache them as an index which is a list of all default objects, and | |
| * then each default object is cached individually. | |
| * | |
| * @return Either an array of cached objects, or NULL indicating a cache | |
| * rebuild is necessary. | |
| */ | |
| function _ctools_export_get_defaults_from_cache($table, $export) { | |
| $data = cache_get('ctools_export_index:' . $table, $export['default cache bin']); | |
| if (!$data || !is_array($data->data)) { | |
| return; | |
| } | |
| // This is the perfectly valid case where there are no default objects, | |
| // and we have cached this state. | |
| if (empty($data->data)) { | |
| return array(); | |
| } | |
| $keys = array(); | |
| foreach ($data->data as $name) { | |
| $keys[] = 'ctools_export:' . $table . ':' . $name; | |
| } | |
| $data = cache_get_multiple($keys, $export['default cache bin']); | |
| // If any of our indexed keys missed, then we have a fail and we need to | |
| // rebuild. | |
| if (!empty($keys)) { | |
| return; | |
| } | |
| // Now, translate the returned cache objects to actual objects. | |
| $cache = array(); | |
| foreach ($data as $cached_object) { | |
| $cache[$cached_object->data->{$export['key']}] = $cached_object->data; | |
| } | |
| return $cache; | |
| } | |
| /** | |
| * Get a limited number of default objects. | |
| * | |
| * This attempts to load the objects directly from cache. If it cannot, | |
| * the cache is rebuilt. This does not disturb the general get defaults | |
| * from cache object. | |
| * | |
| * This function should ONLY be called if default caching is enabled. | |
| * It does not check, it is assumed the caller has already done so. | |
| */ | |
| function _ctools_export_get_some_defaults($table, $export, $names) { | |
| foreach ($names as $name) { | |
| $keys[] = 'ctools_export:' . $table . ':' . $name; | |
| } | |
| $data = cache_get_multiple($keys, $export['default cache bin']); | |
| // Cache hits remove the $key from $keys by reference. Cache | |
| // misses do not. A cache miss indicates we may have to rebuild. | |
| if (!empty($keys)) { | |
| return _ctools_export_get_defaults($table, $export); | |
| } | |
| // Now, translate the returned cache objects to actual objects. | |
| $cache = array(); | |
| foreach ($data as $cached_object) { | |
| $cache[$cached_object->data->{$export['key']}] = $cached_object->data; | |
| } | |
| return $cache; | |
| } | |
| /** | |
| * Unpack data loaded from the database onto an object. | |
| * | |
| * @param $schema | |
| * The schema from drupal_get_schema(). | |
| * @param $data | |
| * The data as loaded from the database. | |
| * @param $object | |
| * If an object, data will be unpacked onto it. If a string | |
| * an object of that type will be created. | |
| */ | |
| function _ctools_export_unpack_object($schema, $data, $object = 'stdClass') { | |
| if (is_string($object)) { | |
| if (class_exists($object)) { | |
| $object = new $object; | |
| } | |
| else { | |
| $object = new stdClass; | |
| } | |
| } | |
| // Go through our schema and build correlations. | |
| foreach ($schema['fields'] as $field => $info) { | |
| if (isset($data->$field)) { | |
| $object->$field = empty($info['serialize']) ? $data->$field : unserialize($data->$field); | |
| } | |
| else { | |
| $object->$field = NULL; | |
| } | |
| } | |
| if (isset($schema['join'])) { | |
| foreach ($schema['join'] as $join_key => $join) { | |
| $join_schema = ctools_export_get_schema($join['table']); | |
| if (!empty($join['load'])) { | |
| foreach ($join['load'] as $field) { | |
| $info = $join_schema['fields'][$field]; | |
| $object->$field = empty($info['serialize']) ? $data->$field : unserialize($data->$field); | |
| } | |
| } | |
| } | |
| } | |
| return $object; | |
| } | |
| /** | |
| * Unpack data loaded from the database onto an object. | |
| * | |
| * @param $table | |
| * The name of the table this object represents. | |
| * @param $data | |
| * The data as loaded from the database. | |
| */ | |
| function ctools_export_unpack_object($table, $data) { | |
| $schema = ctools_export_get_schema($table); | |
| return _ctools_export_unpack_object($schema, $data, $schema['export']['object']); | |
| } | |
| /** | |
| * Export a field. | |
| * | |
| * This is a replacement for var_export(), allowing us to more nicely | |
| * format exports. It will recurse down into arrays and will try to | |
| * properly export bools when it can, though PHP has a hard time with | |
| * this since they often end up as strings or ints. | |
| */ | |
| function ctools_var_export($var, $prefix = '') { | |
| if (is_array($var)) { | |
| if (empty($var)) { | |
| $output = 'array()'; | |
| } | |
| else { | |
| $output = "array(\n"; | |
| foreach ($var as $key => $value) { | |
| $output .= $prefix . " " . ctools_var_export($key) . " => " . ctools_var_export($value, $prefix . ' ') . ",\n"; | |
| } | |
| $output .= $prefix . ')'; | |
| } | |
| } | |
| else if (is_object($var) && get_class($var) === 'stdClass') { | |
| // var_export() will export stdClass objects using an undefined | |
| // magic method __set_state() leaving the export broken. This | |
| // workaround avoids this by casting the object as an array for | |
| // export and casting it back to an object when evaluated. | |
| $output = '(object) ' . ctools_var_export((array) $var, $prefix); | |
| } | |
| else if (is_bool($var)) { | |
| $output = $var ? 'TRUE' : 'FALSE'; | |
| } | |
| else { | |
| $output = var_export($var, TRUE); | |
| } | |
| return $output; | |
| } | |
| /** | |
| * Export an object into code. | |
| */ | |
| function ctools_export_object($table, $object, $indent = '', $identifier = NULL, $additions = array(), $additions2 = array()) { | |
| $schema = ctools_export_get_schema($table); | |
| if (!isset($identifier)) { | |
| $identifier = $schema['export']['identifier']; | |
| } | |
| $output = $indent . '$' . $identifier . ' = new ' . get_class($object) . "();\n"; | |
| if ($schema['export']['can disable']) { | |
| $disabled = !isset($object->disabled) || $object->disabled != TRUE ? 'FALSE' : 'TRUE'; | |
| $output .= $indent . '$' . $identifier . '->disabled = ' . $disabled . '; /* 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: | |
| foreach ($additions as $field => $value) { | |
| $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n"; | |
| } | |
| $fields = $schema['fields']; | |
| if (!empty($schema['join'])) { | |
| foreach ($schema['join'] as $join) { | |
| if (!empty($join['load'])) { | |
| foreach ($join['load'] as $join_field) { | |
| $fields[$join_field] = $join['fields'][$join_field]; | |
| } | |
| } | |
| } | |
| } | |
| // Go through our schema and joined tables and build correlations. | |
| foreach ($fields as $field => $info) { | |
| if (!empty($info['no export'])) { | |
| continue; | |
| } | |
| if (!isset($object->$field)) { | |
| if (isset($info['default'])) { | |
| $object->$field = $info['default']; | |
| } | |
| else { | |
| $object->$field = ''; | |
| } | |
| } | |
| // Note: This is the *field* export callback, not the table one! | |
| if (!empty($info['export callback']) && function_exists($info['export callback'])) { | |
| $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . $info['export callback']($object, $field, $object->$field, $indent) . ";\n"; | |
| } | |
| else { | |
| $value = $object->$field; | |
| if ($info['type'] == 'int') { | |
| if (isset($info['size']) && $info['size'] == 'tiny') { | |
| $info['boolean'] = (!isset($info['boolean'])) ? $schema['export']['boolean'] : $info['boolean']; | |
| $value = ($info['boolean']) ? (bool) $value : (int) $value; | |
| } | |
| else { | |
| $value = (int) $value; | |
| } | |
| } | |
| $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n"; | |
| } | |
| } | |
| // And bottom additions here | |
| foreach ($additions2 as $field => $value) { | |
| $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ";\n"; | |
| } | |
| return $output; | |
| } | |
| /** | |
| * Get the schema for a given table. | |
| * | |
| * This looks for data the export subsystem needs and applies defaults so | |
| * that it's easily available. | |
| */ | |
| function ctools_export_get_schema($table) { | |
| static $drupal_static_fast; | |
| if (!isset($drupal_static_fast)) { | |
| $drupal_static_fast['cache'] = &drupal_static(__FUNCTION__); | |
| } | |
| $cache = &$drupal_static_fast['cache']; | |
| if (empty($cache[$table])) { | |
| $schema = drupal_get_schema($table); | |
| // If our schema isn't loaded, it's possible we're in a state where it | |
| // simply hasn't been cached. If we've been asked, let's force the | |
| // issue. | |
| if (!$schema || empty($schema['export'])) { | |
| // force a schema reset: | |
| $schema = drupal_get_schema($table, TRUE); | |
| } | |
| if (!isset($schema['export'])) { | |
| return array(); | |
| } | |
| if (empty($schema['module'])) { | |
| return array(); | |
| } | |
| // Add some defaults | |
| $schema['export'] += array( | |
| 'key' => 'name', | |
| 'key name' => 'Name', | |
| 'object' => 'stdClass', | |
| 'status' => 'default_' . $table, | |
| 'default hook' => 'default_' . $table, | |
| 'can disable' => TRUE, | |
| 'identifier' => $table, | |
| 'primary key' => !empty($schema['primary key']) ? $schema['primary key'][0] : '', | |
| 'bulk export' => TRUE, | |
| 'list callback' => "$schema[module]_{$table}_list", | |
| 'to hook code callback' => "$schema[module]_{$table}_to_hook_code", | |
| 'cache defaults' => FALSE, | |
| 'default cache bin' => 'cache', | |
| 'export type string' => 'type', | |
| 'boolean' => TRUE, | |
| ); | |
| // If the export definition doesn't have the "primary key" then the CRUD | |
| // save callback won't work. | |
| if (empty($schema['export']['primary key']) && user_access('administer site configuration')) { | |
| drupal_set_message(t('The export definition of @table is missing the "primary key" property.', array('@table' => $table)), 'error'); | |
| } | |
| // Notes: | |
| // The following callbacks may be defined to override default behavior | |
| // when using CRUD functions: | |
| // | |
| // create callback | |
| // load callback | |
| // load multiple callback | |
| // load all callback | |
| // save callback | |
| // delete callback | |
| // export callback | |
| // import callback | |
| // | |
| // See the appropriate ctools_export_crud function for details on what | |
| // arguments these callbacks should accept. Please do not call these | |
| // directly, always use the ctools_export_crud_* wrappers to ensure | |
| // that default implementations are honored. | |
| $cache[$table] = $schema; | |
| } | |
| return $cache[$table]; | |
| } | |
| /** | |
| * Gets the schemas for all tables with ctools object metadata. | |
| */ | |
| function ctools_export_get_schemas($for_export = FALSE) { | |
| $export_tables = &drupal_static(__FUNCTION__); | |
| if (is_null($export_tables)) { | |
| $export_tables = array(); | |
| $schemas = drupal_get_schema(); | |
| foreach ($schemas as $table => $schema) { | |
| if (!isset($schema['export'])) { | |
| unset($schemas[$table]); | |
| continue; | |
| } | |
| $export_tables[$table] = ctools_export_get_schema($table); | |
| } | |
| } | |
| return $for_export ? array_filter($export_tables, '_ctools_export_filter_export_tables') : $export_tables; | |
| } | |
| function _ctools_export_filter_export_tables($schema) { | |
| return !empty($schema['export']['bulk export']); | |
| } | |
| function ctools_export_get_schemas_by_module($modules = array(), $for_export = FALSE) { | |
| $export_tables = array(); | |
| $list = ctools_export_get_schemas($for_export); | |
| foreach ($list as $table => $schema) { | |
| $export_tables[$schema['module']][$table] = $schema; | |
| } | |
| return empty($modules) ? $export_tables : array_keys($export_tables, $modules); | |
| } | |
| /** | |
| * Set the status of a default $object as a variable. | |
| * | |
| * The status, in this case, is whether or not it is 'disabled'. | |
| * This function does not check to make sure $object actually | |
| * exists. | |
| */ | |
| function ctools_export_set_status($table, $name, $new_status = TRUE) { | |
| $schema = ctools_export_get_schema($table); | |
| $status = variable_get($schema['export']['status'], array()); | |
| $status[$name] = $new_status; | |
| variable_set($schema['export']['status'], $status); | |
| } | |
| /** | |
| * Set the status of a default $object as a variable. | |
| * | |
| * This is more efficient than ctools_export_set_status because it | |
| * will actually unset the variable entirely if it's not necessary, | |
| * this saving a bit of space. | |
| */ | |
| function ctools_export_set_object_status($object, $new_status = TRUE) { | |
| $table = $object->table; | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| $status = variable_get($export['status'], array()); | |
| // Compare | |
| if (!$new_status && $object->export_type & EXPORT_IN_DATABASE) { | |
| unset($status[$object->{$export['key']}]); | |
| } | |
| else { | |
| $status[$object->{$export['key']}] = $new_status; | |
| } | |
| variable_set($export['status'], $status); | |
| } | |
| /** | |
| * Provide a form for displaying an export. | |
| * | |
| * This is a simple form that should be invoked like this: | |
| * @code | |
| * $output = drupal_get_form('ctools_export_form', $code, $object_title); | |
| * @endcode | |
| */ | |
| function ctools_export_form($form, &$form_state, $code, $title = '') { | |
| $lines = substr_count($code, "\n"); | |
| $form['code'] = array( | |
| '#type' => 'textarea', | |
| '#title' => $title, | |
| '#default_value' => $code, | |
| '#rows' => $lines, | |
| ); | |
| return $form; | |
| } | |
| /** | |
| * Create a new object based upon schema values. | |
| * | |
| * Because 'default' has ambiguous meaning on some fields, we will actually | |
| * use 'object default' to fill in default values if default is not set | |
| * That's a little safer to use as it won't cause weird database default | |
| * situations. | |
| */ | |
| function ctools_export_new_object($table, $set_defaults = TRUE) { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| $object = new $export['object']; | |
| foreach ($schema['fields'] as $field => $info) { | |
| if (isset($info['object default'])) { | |
| $object->$field = $info['object default']; | |
| } | |
| else if (isset($info['default'])) { | |
| $object->$field = $info['default']; | |
| } | |
| else { | |
| $object->$field = NULL; | |
| } | |
| } | |
| if ($set_defaults) { | |
| // Set some defaults so this data always exists. | |
| // We don't set the export_type property here, as this object is not saved | |
| // yet. We do give it NULL so we don't generate notices trying to read it. | |
| $object->export_type = NULL; | |
| $object->{$export['export type string']} = t('Local'); | |
| } | |
| return $object; | |
| } | |
| /** | |
| * Convert a group of objects to code based upon input and return this as a larger | |
| * export. | |
| */ | |
| function ctools_export_to_hook_code(&$code, $table, $names = array(), $name = 'foo') { | |
| $schema = ctools_export_get_schema($table); | |
| $export = $schema['export']; | |
| // Use the schema-specified function for generating hook code, if one exists | |
| if (function_exists($export['to hook code callback'])) { | |
| $output = $export['to hook code callback']($names, $name); | |
| } | |
| // Otherwise, the following code generates basic hook code | |
| else { | |
| $output = ctools_export_default_to_hook_code($schema, $table, $names, $name); | |
| } | |
| if (!empty($output)) { | |
| if (isset($export['api'])) { | |
| if (isset($code[$export['api']['owner']][$export['api']['api']]['version'])) { | |
| $code[$export['api']['owner']][$export['api']['api']]['version'] = max($code[$export['api']['owner']][$export['api']['api']]['version'], $export['api']['minimum_version']); | |
| } | |
| else { | |
| $code[$export['api']['owner']][$export['api']['api']]['version'] = $export['api']['minimum_version']; | |
| $code[$export['api']['owner']][$export['api']['api']]['code'] = ''; | |
| } | |
| $code[$export['api']['owner']][$export['api']['api']]['code'] .= $output; | |
| } | |
| else { | |
| if (empty($code['general'])) { | |
| $code['general'] = ''; | |
| } | |
| $code['general'] .= $output; | |
| } | |
| } | |
| } | |
| /** | |
| * Default function to export objects to code. | |
| * | |
| * Note that if your module provides a 'to hook code callback' then it will | |
| * receive only $names and $name as arguments. Your module is presumed to | |
| * already know the rest. | |
| */ | |
| function ctools_export_default_to_hook_code($schema, $table, $names, $name) { | |
| $export = $schema['export']; | |
| $output = ''; | |
| $objects = ctools_export_crud_load_multiple($table, $names); | |
| if ($objects) { | |
| $output = "/**\n"; | |
| $output .= " * Implements hook_{$export['default hook']}().\n"; | |
| $output .= " */\n"; | |
| $output .= "function " . $name . "_{$export['default hook']}() {\n"; | |
| $output .= " \${$export['identifier']}s = array();\n\n"; | |
| foreach ($objects as $object) { | |
| $output .= ctools_export_crud_export($table, $object, ' '); | |
| $output .= " \${$export['identifier']}s['" . check_plain($object->{$export['key']}) . "'] = \${$export['identifier']};\n\n"; | |
| } | |
| $output .= " return \${$export['identifier']}s;\n"; | |
| $output .= "}\n"; | |
| } | |
| return $output; | |
| } | |
| /** | |
| * Default function for listing bulk exportable objects. | |
| */ | |
| function ctools_export_default_list($table, $schema) { | |
| $list = array(); | |
| $items = ctools_export_crud_load_all($table); | |
| $export_key = $schema['export']['key']; | |
| foreach ($items as $item) { | |
| // Try a couple of possible obvious title keys: | |
| $keys = array('admin_title', 'title'); | |
| if (isset($schema['export']['admin_title'])) { | |
| array_unshift($keys, $schema['export']['admin_title']); | |
| } | |
| $string = ''; | |
| foreach ($keys as $key) { | |
| if (!empty($item->$key)) { | |
| $string = $item->$key . " (" . $item->$export_key . ")"; | |
| break; | |
| } | |
| } | |
| if (empty($string)) { | |
| $string = $item->$export_key; | |
| } | |
| $list[$item->$export_key] = check_plain($string); | |
| } | |
| return $list; | |
| } |