dfasdf

From: Adrian Rossouw <adrian@developmentseed.org>


---

 includes/export.inc |  602 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 602 insertions(+), 0 deletions(-)
 create mode 100644 includes/export.inc


diff --git includes/export.inc includes/export.inc
new file mode 100644
index 0000000..d57de18
--- /dev/null
+++ includes/export.inc
@@ -0,0 +1,602 @@
+<?php
+// $Id: $
+
+/**
+ * @file
+ * Contains code to make it easier to have exportable objects.
+ */
+
+/**
+ * @defgroup exportables Exportable objects
+ * @{
+ *
+ * These are objects that can live in code OR the database, and versions in
+ * the database will override versions that are in code.
+ *
+ * This doesn't include a write routine since drupal_write_record is
+ * more or less sufficient.
+ *
+ * Exportable objects are created by adding definition to the schema
+ * in an 'export' section. The following fields are supported in this
+ * chunk of code:
+ * - key: This is the primary key of the exportable object and should be
+ *   a string as names are more portable across systems. It is possible
+ *   to use numbers here, but be aware that export collisions are very
+ *   likely. Defaults to 'name'.
+ * - object: The class the object should be created as. Defaults as stdClass.
+ * - can disable: Control whether or not the exportable objects can be
+ *   disabled. All this does is cause the 'disabled' field on the object
+ *   to always be set appropriately, and a variable is kept to record
+ *   the state. Changes made to this state must be handled by the owner
+ *   of the object. Defaults to TRUE.
+ * - status: Exportable objects can be enabled or disabled, and this status
+ *   is stored in a variable. This defines what variable that is. Defaults
+ *   to: 'default_' . $table,
+ * - default hook: What hook to invoke to find exportable objects that are
+ *   currently defined. These will all be gathered into a giant array.
+ *   Defaults to 'default_' . $table,
+ * - identifier: When exporting the object, the identifier is the variable that
+ *   the exported object will be placed in. Defaults to $table.
+ * - bulk export': Declares whether or not the exportable will be available
+ *   for bulk exporting. (Bulk export UI is currently left to be handled
+ *   by contrib, though the core hooks are present so this can be done.).
+ * - export callback: The callback to use for bulk exporting. Defaults to
+ *   $module . '_export_' . $table.
+ * - list callback: Bulk export callback to provide a list of exportable
+ *   objects to be chosen for bulk exporting. Defaults to
+ *   $module . '_' . $table . '_list'.
+ * - to hook code callback: Function used to generate an export for the bulk
+ *   export process. This is only necessary if the export is more complicated
+ *   than simply listing the fields. Defaults to $module . '_' . $table .
+ *   '_to_hook_code'.
+ *
+ * To load exportable objects, use the export_load_object() function. To
+ * write exportable objects to the database, use drupal_write_record().
+ */
+
+/**
+ * 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);
+
+/**
+ * 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.
+ * @return
+ *   An array of loaded objects, or NULL.
+ */
+function export_load_object($table, $type = 'all', $args = array()) {
+  static $cache = array();
+  static $cached_database = array();
+
+  $schema = export_get_schema($table);
+  $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 $name) {
+      if (isset($cache[$table][$name])) {
+        $return[$name] = $cache[$table][$name];
+        unset($args[$name]);
+      }
+    }
+
+    // If nothing left to load, return the result.
+    if (empty($args)) {
+      return $return;
+    }
+  }
+
+  // Build the query
+  $query = db_select($table, 'export');
+  $query->fields('export');
+  // 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) {
+      $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) {
+    $object = _export_unpack_object($schema, $data, $export['object']);
+    $object->table = $table;
+    $object->type = t('Normal');
+    $object->export_type = EXPORT_IN_DATABASE;
+    // Determine if default object is enabled or disabled.
+    if (isset($status[$object->name])) {
+      $object->disabled = $status[$object->name];
+    }
+
+    $cache[$table][$object->{$export['key']}] = $object;
+    if ($type == 'conditions') {
+      $return[$object->{$export['key']}] = $object;
+    }
+  }
+
+  if ($defaults = _export_get_defaults($table, $export)) {
+
+    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) || $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']}]->type = t('Overridden');
+        $cache[$table][$object->{$export['key']}]->export_type |= EXPORT_IN_CODE;
+        if ($type == 'conditions') {
+          $return[$object->{$export['key']}] = $cache[$table][$object->{$export['key']}];
+        }
+      }
+      else {
+        $object->type = 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;
+}
+
+/**
+ * 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 export_get_default_object($table, $name) {
+  $schema = export_get_schema($table);
+  $export = $schema['export'];
+
+  if (!$export['default hook']) {
+    return;
+  }
+
+  $defaults = _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->name])) {
+    $object->disabled = $status[$object->name];
+  }
+
+  $object->type = 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 _export_get_defaults($table, $export) {
+  static $cache = array();
+
+  if (!isset($cache[$table])) {
+    $cache[$table] = array();
+
+    if ($export['default hook']) {
+      $modules = module_implements($export['default hook']);
+    }
+
+    foreach ($modules as $module) {
+      $function = $module . '_' . $export['default hook'];
+      if (function_exists($function)) {
+        $cache[$table] += (array) $function($export);
+      }
+    }
+
+    drupal_alter($export['default hook'], $cache[$table]);
+  }
+
+  return $cache[$table];
+}
+
+/**
+ * Unpack data loaded from the database onto an object.
+ *
+ * @param $schema
+ *   The schema from drupal_get_schema().
+ * @param $data
+ *   The data as loaded by db_fetch_object().
+ * @param $object
+ *   If an object, data will be unpacked onto it. If a string
+ *   an object of that type will be created.
+ */
+function _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) {
+    $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 by db_fetch_object().
+ */
+function export_unpack_object($table, $data) {
+  $schema = export_get_schema($table);
+  return _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 export_var_export($var, $prefix = '') {
+  if (is_array($var)) {
+    if (empty($var)) {
+      $output = 'array()';
+    }
+    else {
+      $output = "array(\n";
+      foreach ($var as $key => $value) {
+        $output .= "  '$key' => " . export_var_export($value, '  ') . ",\n";
+      }
+      $output .= ')';
+    }
+  }
+  else if (is_bool($var)) {
+    $output = $var ? 'TRUE' : 'FALSE';
+  }
+  else {
+    $output = var_export($var, TRUE);
+  }
+
+  if ($prefix) {
+    $output = str_replace("\n", "\n$prefix", $output);
+  }
+
+  return $output;
+}
+
+/**
+ * Export an object into code.
+ */
+function export_object($table, $object, $indent = '', $identifier = NULL, $additions = array(), $additions2 = array()) {
+  $schema = export_get_schema($table);
+  if (!isset($identifier)) {
+    $identifier = $schema['export']['identifier'];
+  }
+
+  $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";
+  }
+
+  // Put top additions here:
+  foreach ($additions as $field => $value) {
+    $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . export_var_export($value, $indent) . ";\n";
+  }
+
+  // Go through our schema and build correlations.
+  foreach ($schema['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 = '';
+      }
+    }
+
+    $value = $object->$field;
+    if ($info['type'] == 'int') {
+      $value = (isset($info['size']) && $info['size'] == 'tiny') ? (bool) $value : (int) $value;
+    }
+
+    $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . export_var_export($value, $indent) . ";\n";
+  }
+
+  // And bottom additions here
+  foreach ($additions2 as $field => $value) {
+    $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . export_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 export_get_schema($table) {
+  $schema = drupal_get_schema($table);
+
+  if (!isset($schema['export'])) {
+    $schema['export'] = array();
+  }
+
+  // Add some defaults
+  $schema['export'] += array(
+    'key' => 'name',
+    'object' => 'stdClass',
+    'status' => 'default_' . $table,
+    'default hook' => 'default_' . $table,
+    'can disable' => TRUE,
+    'identifier' => $table,
+    'bulk export' => TRUE,
+    'export callback' => "$schema[module]_export_{$table}",
+    'list callback' => "$schema[module]_{$table}_list",
+    'to hook code callback' => "$schema[module]_{$table}_to_hook_code",
+  );
+
+  return $schema;
+}
+
+/**
+ * Gets the schemas for all tables with export object metdata.
+ */
+function export_get_schemas($for_export = FALSE) {
+  static $export_tables;
+  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] = export_get_schema($table);
+    }
+  }
+  return $for_export ? array_filter($export_tables, '_export_filter_export_tables') : $export_tables;
+}
+
+function _export_filter_export_tables($export) {
+  return empty($export['bulk export']);
+}
+
+function export_get_schemas_by_module($modules = array(), $for_export = FALSE) {
+  $export_tables = array();
+  $list = 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 export_set_status($table, $name, $new_status = TRUE) {
+  $schema = 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 export_set_status because it
+ * will actually unset the variable entirely if it's not necessary,
+ * this saving a bit of space.
+ */
+function export_set_object_status($object, $new_status = TRUE) {
+  $table = $object->table;
+  $schema = export_get_schema($table);
+  $status = variable_get($schema['export']['status'], array());
+
+  // Compare
+  if (!$new_status && $object->export_type & EXPORT_IN_DATABASE) {
+    unset($status[$object->name]);
+  }
+  else {
+    $status[$object->name] = $new_status;
+  }
+
+  variable_set($schema['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('export_form', $code, $object_title);
+ * @endcode
+ */
+function export_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 export_new_object($table, $set_defaults = TRUE) {
+  $schema = 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.
+    $object->export_type = EXPORT_IN_DATABASE;
+    $object->type = t('Local');
+  }
+  return $object;
+}
+
+/**
+ * Convert a group of objects to code based upon input and return this as a larger
+ * export.
+ */
+function export_to_hook_code(&$code, $table, $names = array(), $name = 'foo') {
+  $schema = 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 {
+    $objects = export_load_object($table, 'names', $names);
+    if ($objects) {
+      $output = "/**\n";
+      $output .= " * Implementation of hook_{$export['default hook']}()\n";
+      $output .= " */\n";
+      $output .= "function " . $name . "_{$export['default hook']}() {\n";
+      foreach ($objects as $object) {
+        $output .= $export['export callback']($object, '  '); // if this function does not exist, better to error out than fail silently
+        $output .= "  \${$export['identifier']}s['" . check_plain($object->$export['key']) . "'] = \${$export['identifier']};\n\n";
+      }
+      $output .= " return \${$export['identifier']}s;\n";
+      $output .= "}\n";
+    }
+  }
+
+  if (!empty($output)) {
+    if (empty($code['general'])) {
+      $code['general'] = '';
+    }
+    $code['general'] .= $output;
+  }
+}
+
+/**
+ * @} End of "defgroup exportables".
+ */
