Browse Source

updated features

Bachir Soussi Chiadmi 8 years ago
parent
commit
e2fde76aff

+ 15 - 11
sites/all/modules/contrib/admin/features/features.admin.inc

@@ -88,6 +88,12 @@ function features_settings_form($form, $form_state) {
     '#default_value' => variable_get('features_rebuild_on_flush', TRUE),
     '#description' => t('If you have a large site with many features, you may experience lag on full cache clear. If disabled, features will rebuild only when viewing the features list or saving the modules list.'),
   );
+  $form['general']['features_rebuild_modules_page'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Rebuild features on accessing modules list page'),
+    '#default_value' => variable_get('features_rebuild_modules_page', FALSE),
+    '#description' => t('If you have a large site with many features, you may experience lag on accessing the modules administration page. If disabled, features will not rebuild when viewing the modules list.'),
+  );
 
   return system_settings_form($form);
 }
@@ -109,7 +115,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
 
   $feature_name = !empty($feature->name) ? $feature->name : '';
   $form = array(
-    '#attributes' => array('class' => array('features-export-form')),
+    '#attributes' => array('class' => array('features-export-form', 'clearfix')),
     '#feature' => isset($feature) ? $feature : NULL,
   );
   $form['info'] = array(
@@ -756,7 +762,7 @@ function features_export_form_rebuild($form, &$form_state) {
 
 function features_export_components_json($feature_name) {
   module_load_include('inc', 'features', 'features.export');
-  $export = array();
+  $export = array('features' => array());
   if (!empty($_POST['items'])) {
     $excluded = (!empty($_POST['excluded'])) ? $_POST['excluded'] : array();
     $stub = array();
@@ -1143,7 +1149,7 @@ function features_admin_form($form, $form_state) {
   // As of 7.0 beta 2 it matters where the "vertical_tabs" element lives on the
   // the array. We add it late, but at the beginning of the array because that
   // keeps us away from trouble.
-  $form = array('packages' => array('#type' => 'vertical_tabs')) + $form;
+  $form = array_merge(array('packages' => array('#type' => 'vertical_tabs')), $form);
 
   $form['buttons'] = array(
     '#theme' => 'features_form_buttons',
@@ -1328,7 +1334,7 @@ function features_form_submit(&$form, &$form_state) {
   // page callback rather than as part of the submit handler as some modules
   // have includes/other directives of importance in hooks that have already
   // been called in this page load.
-  $form_state['redirect'] = 'admin/structure/features/cleanup/clear';
+  $form_state['redirect'] = array('admin/structure/features/cleanup', array('query' => array('token' => drupal_get_token())));
 
   $features = $form['#features'];
   if (!empty($features)) {
@@ -1352,21 +1358,19 @@ function features_form_rebuild() {
 }
 
 /**
- * Form for clearing cache after enabling a feature.
+ * Callback for clearing cache after enabling a feature.
  */
-function features_cleanup_form($form, $form_state, $cache_clear = FALSE) {
-  // Clear caches if we're getting a post-submit redirect that requests it.
-  if ($cache_clear) {
+function features_cleanup() {
+  if (!empty($_GET['token']) && drupal_valid_token($_GET['token'])) {
     drupal_flush_all_caches();
-
     // The following functions need to be run because drupal_flush_all_caches()
     // runs rebuilds in the wrong order. The node type cache is rebuilt *after*
     // the menu is rebuilt, meaning that the menu tree is stale in certain
     // circumstances after drupal_flush_all_caches(). We rebuild again.
     menu_rebuild();
-  }
-
     drupal_goto('admin/structure/features');
+  }
+  return MENU_NOT_FOUND;
 }
 
 /**

+ 2 - 2
sites/all/modules/contrib/admin/features/features.api.php

@@ -42,7 +42,7 @@
  *   are declared "dynamically" or are part of a family of components.
  *
  *   'alter_type': What type of alter hook this hook uses. 'normal' is called
- *   after the main hook is called. 'inline' is embeded within the default hook
+ *   after the main hook is called. 'inline' is embedded within the default hook
  *   and may not be implemented by some default hooks.
  *   'none' is no alter hook exists. Defaults to 'normal'
  *
@@ -310,7 +310,7 @@ function hook_features_pipe_COMPONENT_alter(&$pipe, $data, $export) {
  * The module being exported contained in $export['module_name'].
  */
 function hook_features_pipe_alter(&$pipe, $data, $export) {
-  if ($export['component'] == 'node' && in_array($data, 'my-node-type')) {
+  if ($export['component'] == 'node' && in_array('my-node-type', $data)) {
     $pipe['dependencies'][] = 'mymodule';
   }
 }

+ 36 - 13
sites/all/modules/contrib/admin/features/features.drush.inc

@@ -32,6 +32,12 @@ function features_drush_command() {
     ),
     'drupal dependencies' => array('features'),
     'aliases' => array('fl', 'features'),
+    'outputformat' => array(
+      'default' => 'table',
+      'pipe-format' => 'list',
+      'field-labels' => array('name' => 'Name', 'feature' => 'Feature', 'status' => 'Status', 'version' => 'Version', 'state' => 'State'),
+      'output-data-type' => 'format-table',
+    ),
   );
   $items['features-export'] = array(
     'description' => "Export a feature from your site into a module.",
@@ -207,12 +213,12 @@ function drush_features_list() {
   }
 
   module_load_include('inc', 'features', 'features.export');
-  $rows = array(array(dt('Name'), dt('Feature'), dt('Status'), dt('Version'), dt('State')));
 
   // Sort the Features list before compiling the output.
   $features = features_get_features(NULL, TRUE);
   ksort($features);
 
+  $rows = array();
   foreach ($features as $k => $m) {
     switch (features_get_storage($m->name)) {
       case FEATURES_DEFAULT:
@@ -230,16 +236,19 @@ function drush_features_list() {
       ($m->status == 0 && ($status == 'all' || $status == 'disabled')) ||
       ($m->status == 1 && ($status == 'all' || $status == 'enabled'))
     ) {
-      $rows[] = array(
-        $m->info['name'],
-        $m->name,
-        $m->status ? dt('Enabled') : dt('Disabled'),
-        $m->info['version'],
-        $storage
+      $rows[$k] = array(
+        'name' => $m->info['name'],
+        'feature' => $m->name,
+        'status' => $m->status ? dt('Enabled') : dt('Disabled'),
+        'version' => $m->info['version'],
+        'state' => $storage
       );
     }
   }
-  drush_print_table($rows, TRUE);
+  if (version_compare(DRUSH_VERSION, '6.0', '<')) {
+    drush_print_table($rows, TRUE);
+  }
+  return $rows;
 }
 
 /**
@@ -337,8 +346,13 @@ function _drush_features_component_filter($all_components, $patterns = array(),
     // Rewrite * to %. Let users use both as wildcard.
     $pattern = strtr($pattern, array('*' => '%'));
     $sources = array();
-    $source_pattern = strtok($pattern, ':');
-    $component_pattern = strtok(':');
+    if (strpos($pattern, ':') !== FALSE) {
+      list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
+    }
+    else {
+      $source_pattern = $pattern;
+      $component_pattern = '';
+    }
     // If source is empty, use a pattern.
     if ($source_pattern == '') {
       $source_pattern = '%';
@@ -607,9 +621,18 @@ function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
       drush_op('mkdir', $directory);
     }
     if (is_dir($directory)) {
+      // Ensure that the export will be created in the English language.
+      // The export language must be set before flushing caches as that can
+      // result into translatables being statically cached.
+      $language = _features_export_language();
+
       drupal_flush_all_caches();
       $export = _drush_features_generate_export($info, $module_name);
       $files = features_export_render($export, $module_name, TRUE);
+
+      // Restore the language
+      _features_export_language($language);
+
       // Copy any files if _files key is there.
       if (!empty($files['_files'])) {
         foreach ($files['_files'] as $file_name => $file_info) {
@@ -685,7 +708,7 @@ function _drush_features_generate_export(&$info, &$module_name) {
       }
       else {
         // Split version number parts.
-        $pattern = '/([0-9]-[a-z]+([0-9])+)/';
+        $pattern = '/([0-9]-[a-z]+([0-9]+))/';
         $matches = array();
         preg_match($pattern, $version_minor, $matches);
         $number = array_pop($matches);
@@ -797,7 +820,7 @@ function drush_features_revert() {
     }
   }
   else {
-    drush_features_list();
+    drush_print_table(drush_features_list());
     return;
   }
 }
@@ -851,7 +874,7 @@ function drush_features_revert_all() {
  */
 function drush_features_diff() {
   if (!$args = func_get_args()) {
-    drush_features_list();
+    drush_print_table(drush_features_list());
     return;
   }
   $module = $args[0];

+ 52 - 76
sites/all/modules/contrib/admin/features/features.export.inc

@@ -46,7 +46,7 @@ function features_populate($info, $module_name) {
  */
 function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
   // Ensure that the export will be created in the english language.
-  _features_set_export_language();
+  $language = _features_export_language();
 
   if ($reset) {
     drupal_static_reset(__FUNCTION__);
@@ -92,6 +92,7 @@ function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE)
       }
     }
   }
+  _features_export_language($language);
   return $export;
 }
 
@@ -843,6 +844,13 @@ function features_get_default($component, $module_name = NULL, $alter = TRUE, $r
 
 /**
  * Get a map of components to their providing modules.
+ *
+ * @param string $component
+ * @param string $attribute
+ * @param callable $callback
+ * @param bool $reset
+ *
+ * @return array|bool
  */
 function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
   $map = &drupal_static(__FUNCTION__, array());
@@ -891,6 +899,9 @@ function features_get_default_map($component, $attribute = NULL, $callback = NUL
  * Retrieve an array of features/components and their current states.
  */
 function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) {
+  // Ensure that the export will be created in the English language.
+  $language = _features_export_language();
+
   if ($reset) {
     drupal_static_reset(__FUNCTION__);
   }
@@ -901,7 +912,7 @@ function features_get_component_states($features = array(), $rebuild_only = TRUE
 
   // Retrieve only rebuildable components if requested.
   features_include();
-  $components = array_keys(features_get_components());
+  $components = array_keys(features_get_components(NULL, NULL, $reset));
   if ($rebuild_only) {
     foreach ($components as $k => $component) {
       if (!features_hook($component, 'features_rebuild')) {
@@ -984,6 +995,9 @@ function features_get_component_states($features = array(), $rebuild_only = TRUE
   foreach ($return as $k => $v) {
     $return[$k] = array_intersect_key($return[$k], array_flip($components));
   }
+
+  _features_export_language($language);
+
   return $return;
 }
 
@@ -1006,17 +1020,14 @@ function _features_linetrim($code) {
  * @param bool $remove_empty if set, remove null or empty values for assoc arrays.
  */
 function features_sanitize(&$array, $component = NULL, $remove_empty = TRUE) {
-  // make a deep copy of data to prevent problems when removing recursion later.
-  $array = unserialize(serialize($array));
+  $array = features_remove_recursion($array);
   if (isset($component)) {
     $ignore_keys = _features_get_ignore_keys($component);
     // remove keys to be ignored
-    // doing this now allows us to better control which recursive parts are removed
     if (count($ignore_keys)) {
       _features_remove_ignores($array, $ignore_keys);
     }
   }
-  features_remove_recursion($array);
   _features_sanitize($array, $remove_empty);
 }
 
@@ -1069,81 +1080,45 @@ function _features_is_assoc($array) {
 /**
  * Removes recursion from an object or array.
  *
- * @param $item
- *   An object or array passed by reference.
- */
-function features_remove_recursion(&$item) {
-  $uniqid = __FUNCTION__ . mt_rand(); // use of uniqid() here impacts performance
-  $stack = array();
-  return _features_remove_recursion($item, $stack, $uniqid);
-}
-
-/**
- * Helper to removes recursion from an object/array.
+ * Taken from https://code.google.com/p/formaldehyde/source/browse/trunk/formaldehyde.php
+ * Also used in node_export module
  *
- * @param $item
- *   An object or array passed by reference.
+ * @param $o mixed
+ * @return mixed
+ *   returns a copy of the object or array with recursion removed
  */
-function _features_remove_recursion(&$object, &$stack = array(), $uniqid) {
-  if ((is_object($object) || is_array($object)) && $object) {
-    $in_stack = FALSE;
-    foreach ($stack as &$item) {
-      if (_features_is_ref_to($object, $item, $uniqid)) {
-        $in_stack = TRUE;
-        break;
-      }
-    }
-    unset($item);
-
-    if (!$in_stack) {
-      $stack[] = $object;
-      foreach ($object as $key => &$subobject) {
-        if (_features_remove_recursion($subobject, $stack, $uniqid)) {
-          if (is_object($object)) {
-            unset($object->$key);
-          }
-          else {
-            unset($object[$key]);
-          }
+function features_remove_recursion($o) {
+  static $replace;
+  if (!isset($replace)) {
+    $replace = create_function(
+      '$m',
+      '$r="\x00{$m[1]}ecursion_features";return \'s:\'.strlen($r.$m[2]).\':"\'.$r.$m[2].\'";\';'
+    );
+  }
+  if (is_array($o) || is_object($o)) {
+    $re = '#(r|R):([0-9]+);#';
+    $serialize = serialize($o);
+    if (preg_match($re, $serialize)) {
+      $last = $pos = 0;
+      while (false !== ($pos = strpos($serialize, 's:', $pos))) {
+        $chunk = substr($serialize, $last, $pos - $last);
+        if (preg_match($re, $chunk)) {
+          $length = strlen($chunk);
+          $chunk = preg_replace_callback($re, $replace, $chunk);
+          $serialize = substr($serialize, 0, $last) . $chunk . substr($serialize, $last + ($pos - $last));
+          $pos += strlen($chunk) - $length;
         }
+        $pos += 2;
+        $last = strpos($serialize, ':', $pos);
+        $length = substr($serialize, $pos, $last - $pos);
+        $last += 4 + $length;
+        $pos = $last;
       }
-      unset($subobject);
-    }
-    else {
-      return TRUE;
+      $serialize = substr($serialize, 0, $last) . preg_replace_callback($re, $replace, substr($serialize, $last));
+      $o = unserialize($serialize);
     }
   }
-  return FALSE;
-}
-
-/**
- * Helper function in determining equality of arrays. Credit to http://stackoverflow.com/a/4263181
- *
- * @see _features_remove_recursion()
- *
- * @param $a
- *  object a
- * @param $b
- *  object b
- * @return bool
- *
- */
-function _features_is_ref_to(&$a, &$b, $uniqid) {
-  if (is_object($a) && is_object($b)) {
-    return ($a === $b);
-  }
-
-  $temp_a = $a;
-  $temp_b = $b;
-
-  $b = $uniqid;
-
-  if ($a === $uniqid) $return = true;
-  else $return = false;
-
-  $a = $temp_a;
-  $b = $temp_b;
-  return $return;
+  return $o;
 }
 
 /**
@@ -1162,7 +1137,7 @@ function _features_remove_ignores(&$item, $ignore_keys, $level = -1) {
   if (!is_array($item) && !is_object($item)) {
     return;
   }
-  foreach ($item as $key => $value) {
+  foreach ($item as $key => &$value) {
     if (isset($ignore_keys[$key]) && ($ignore_keys[$key] == $level)) {
       if ($is_object) {
         unset($item->$key);
@@ -1175,6 +1150,7 @@ function _features_remove_ignores(&$item, $ignore_keys, $level = -1) {
       _features_remove_ignores($value, $ignore_keys, $level+1);
     }
   }
+  unset($value);
 }
 
 /**

+ 7 - 3
sites/all/modules/contrib/admin/features/features.info

@@ -3,12 +3,16 @@ description = "Provides feature management for Drupal."
 core = 7.x
 package = "Features"
 files[] = tests/features.test
+test_dependencies[] = image
+test_dependencies[] = strongarm
+test_dependencies[] = taxonomy
+test_dependencies[] = views
 
 configure = admin/structure/features/settings
 
-; Information added by Drupal.org packaging script on 2015-06-24
-version = "7.x-2.6"
+; Information added by Drupal.org packaging script on 2016-04-18
+version = "7.x-2.10"
 core = "7.x"
 project = "features"
-datestamp = "1435165997"
+datestamp = "1461011641"
 

+ 31 - 68
sites/all/modules/contrib/admin/features/features.module

@@ -84,8 +84,7 @@ function features_menu() {
   $items['admin/structure/features/cleanup'] = array(
     'title' => 'Cleanup',
     'description' => 'Clear cache after enabling/disabling a feature.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('features_cleanup_form', 4),
+    'page callback' => 'features_cleanup',
     'type' => MENU_CALLBACK,
     'file' => 'features.admin.inc',
     'weight' => 1,
@@ -128,7 +127,7 @@ function features_menu() {
     'description' => 'Display components of a feature.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('features_admin_components', 3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
@@ -147,7 +146,7 @@ function features_menu() {
     'description' => 'Recreate an existing feature.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('features_export_form', 3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'type' => MENU_LOCAL_TASK,
@@ -160,7 +159,7 @@ function features_menu() {
       'description' => 'Compare default and current feature.',
       'page callback' => 'features_feature_diff',
       'page arguments' => array(3, 5),
-      'load arguments' => array(3, TRUE),
+      'load arguments' => array(TRUE),
       'access callback' => 'features_access_override_actions',
       'access arguments' => array(3),
       'type' => MENU_LOCAL_TASK,
@@ -173,7 +172,7 @@ function features_menu() {
     'description' => 'Lock a feature or components.',
     'page callback' => 'features_admin_lock',
     'page arguments' => array(3, 5, 6),
-    'load arguments' => array(3, TRUE, TRUE),
+    'load arguments' => array(TRUE),
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
     'file' => 'features.admin.inc',
@@ -183,7 +182,7 @@ function features_menu() {
     'description' => 'Javascript status call back.',
     'page callback' => 'features_feature_status',
     'page arguments' => array(3),
-    'load arguments' => array(3, TRUE),
+    'load arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('administer features'),
     'type' => MENU_CALLBACK,
@@ -278,7 +277,11 @@ function features_flush_caches() {
       features_get_modules(NULL, TRUE);
     }
   }
-  return array('cache_features');
+
+  if (db_table_exists('cache_features')) {
+    return array('cache_features');
+  }
+  return array();
 }
 
 /**
@@ -515,7 +518,7 @@ function features_load_feature($name, $reset = FALSE) {
  * Return a module 'object' including .info information.
  *
  * @param $name
- *   The name of the module to retrieve information for. If ommitted,
+ *   The name of the module to retrieve information for. If omitted,
  *   an array of all available modules will be returned.
  * @param $reset
  *   Whether to reset the cache.
@@ -757,9 +760,9 @@ function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
     $cache->data = $data;
   }
   if (!empty($name)) {
-    return !empty($cache->data[$type][$name]) ? clone $cache->data[$type][$name] : array();
+    return !empty($cache->data[$type][$name]) ? clone $cache->data[$type][$name] : FALSE;
   }
-  return !empty($cache->data[$type]) ? $cache->data[$type] : array();
+  return !empty($cache->data[$type]) ? $cache->data[$type] : FALSE;
 }
 
 /**
@@ -906,7 +909,7 @@ function features_access_override_actions($feature) {
 
       features_include();
       module_load_include('inc', 'features', 'features.export');
-      $access[$feature->name] = in_array(features_get_storage($feature->name), array(FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW)) && user_access('administer features');
+      $access[$feature->name] = in_array(features_get_storage($feature->name), array(FEATURES_DEFAULT, FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW));
     }
     return $access[$feature->name];
   }
@@ -917,7 +920,9 @@ function features_access_override_actions($feature) {
  * Implements hook_form_alter() for system_modules form().
  */
 function features_form_system_modules_alter(&$form) {
-  features_rebuild();
+  if (variable_get('features_rebuild_modules_page', FALSE)) {
+    features_rebuild();
+  }
 }
 
 /**
@@ -1219,12 +1224,14 @@ function features_feature_unlock($feature, $component = NULL) {
 }
 
 /**
- * Sets the current language to english to ensure a proper export.
+ * Sets/Returns the current language to english to ensure a proper export.
  */
-function _features_set_export_language() {
-  // Ensure this is only done if the language isn't already en.
-  // This can be called multiple times - ensure the handling is done just once.
-  if ($GLOBALS['language']->language != 'en' && !drupal_static(__FUNCTION__)) {
+function _features_export_language($language = NULL) {
+  $current = $GLOBALS['language'];
+  if (isset($language)) {
+    $GLOBALS['language'] = $language;
+  }
+  elseif ($GLOBALS['language']->language != 'en') {
     // Create the language object as language_default() does.
     $GLOBALS['language'] = (object) array(
       'language' => 'en',
@@ -1239,57 +1246,8 @@ function _features_set_export_language() {
       'weight' => 0,
       'javascript' => '',
     );
-    // Ensure that static caches are cleared, as they might contain language
-    // specific information. But keep some important ones. The call below
-    // accesses a non existing key and requests to reset it. In such cases the
-    // whole caching data array is returned.
-    $static = drupal_static(uniqid('', TRUE), NULL, TRUE);
-    drupal_static_reset();
-    // Restore some of the language independent, runtime state information to
-    // keep everything working and avoid unnecessary double processing.
-    $static_caches_to_keep = array(
-      'conf_path',
-      'system_list',
-      'ip_address',
-      'drupal_page_is_cacheable',
-      'list_themes',
-      'drupal_page_header',
-      'drupal_send_headers',
-      'drupal_http_headers',
-      'language_list',
-      'module_implements',
-      'drupal_alter',
-      'path_is_admin',
-      'path_get_admin_paths',
-      'drupal_match_path',
-      'menu_get_custom_theme',
-      'menu_get_item',
-      'arg',
-      'drupal_system_listing',
-      'drupal_parse_info_file',
-      'libraries_get_path',
-      'module_hook_info',
-      'drupal_add_js',
-      'drupal_add_js:jquery_added',
-      'drupal_add_library',
-      'drupal_get_library',
-      'drupal_add_css',
-      'menu_set_active_trail',
-      'menu_link_get_preferred',
-      'menu_set_active_menu_names',
-      'theme_get_registry',
-      'features_get_components',
-      'features_get_components_by_key',
-    );
-    foreach ($static_caches_to_keep as $cid) {
-      if (isset($static[$cid])) {
-        $data = &drupal_static($cid);
-        $data = $static[$cid];
-      }
-    }
-    $called = &drupal_static(__FUNCTION__);
-    $called = TRUE;
   }
+  return $current;
 }
 
 /**
@@ -1326,6 +1284,11 @@ function features_features_ignore($component) {
     case 'field':
       $ignores['locked'] = 1;
       break;
+    case 'field_base':
+      $ignores['indexes'] = 0;
+      break;
+    case 'taxonomy':
+      $ignores['hierarchy'] = 0;
   }
   return $ignores;
 }

+ 115 - 0
sites/all/modules/contrib/admin/features/includes/features.contact.inc

@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * @file
+ * Features integration for 'contact' module.
+ */
+
+/**
+ * Implements hook_features_api().
+ */
+function contact_features_api() {
+  return array(
+    'contact_categories' => array(
+      'name' => t('Contact categories'),
+      'feature_source' => TRUE,
+      'default_hook' => 'contact_categories_defaults',
+      'default_file' => FEATURES_DEFAULTS_INCLUDED,
+    ),
+  );
+}
+
+/**
+ * Implements hook_features_export_options().
+ */
+function contact_categories_features_export_options() {
+  $options = array();
+  $categories = db_select('contact', 'c')->fields('c')->execute()->fetchAll();
+  foreach ($categories as $category) {
+    $options["$category->category"] = "$category->category";
+  }
+  return $options;
+}
+
+/**
+ * Implements hook_features_export().
+ */
+function contact_categories_features_export($data, &$export, $module_name = '') {
+  $export['dependencies']['features'] = 'features';
+  $export['dependencies']['contact'] = 'contact';
+
+  foreach ($data as $name) {
+    $export['features']['contact_categories'][$name] = $name;
+  }
+
+  return array();
+}
+
+/**
+ * Implements hook_features_export_render().
+ */
+function contact_categories_features_export_render($module, $data, $export = NULL) {
+  $render = array();
+  foreach ($data as $name) {
+    $export_category = db_select('contact', 'c')
+      ->fields('c', array('cid', 'category'))
+      ->condition('category', $name, 'LIKE')
+      ->execute()
+      ->fetchAll();
+    if (isset($export_category[0]->cid) && ($category = contact_load($export_category[0]->cid))) {
+      unset($category['cid']);
+      $render[$name] = $category;
+    }
+  }
+  return array('contact_categories_defaults' => '  return ' . features_var_export($render, '  ') . ';');
+}
+
+/**
+ * Implements hook_features_revert().
+ */
+function contact_categories_features_revert($module) {
+  return contact_categories_features_rebuild($module);
+}
+
+/**
+ * Implements hook_features_rebuild().
+ */
+function contact_categories_features_rebuild($module) {
+  if ($defaults = features_get_default('contact_categories', $module)) {
+    foreach ($defaults as $default_category) {
+      $existing_categories = db_select('contact', 'c')
+        ->fields('c', array('cid', 'category'))
+        ->execute()
+        ->fetchAll();
+      if ($existing_categories) {
+        foreach ($existing_categories as $existing_category) {
+          if ($default_category['category'] == $existing_category->category) {
+            db_update('contact')
+              ->fields(
+                array(
+                  'recipients' => $default_category['recipients'],
+                  'reply' => $default_category['reply'],
+                  'weight' => $default_category['weight'],
+                  'selected' => $default_category['selected'],
+                )
+              )
+              ->condition('category', $existing_category->category, '=')
+              ->execute();
+          }
+          else {
+            db_merge('contact')
+              ->key(array('category' => $default_category['category']))
+              ->fields($default_category)
+              ->execute();
+          }
+        }
+      }
+      else {
+        db_merge('contact')
+          ->key(array('category' => $default_category['category']))
+          ->fields($default_category)
+          ->execute();
+      }
+    }
+  }
+}

+ 20 - 9
sites/all/modules/contrib/admin/features/includes/features.field.inc

@@ -158,7 +158,7 @@ function field_base_features_export_render($module, $data, $export = NULL) {
         unset($field['storage']);
       }
       // If we still have a storage declaration here it means that a non-default
-      // storage type was altered into to the field definition. And noone would
+      // storage type was altered into to the field definition. And no one would
       // never need to change the 'details' key, so don't render it.
       if (isset($field['storage']['details'])) {
         unset($field['storage']['details']);
@@ -170,10 +170,10 @@ function field_base_features_export_render($module, $data, $export = NULL) {
       $field_identifier = features_var_export($identifier);
       if (features_field_export_needs_wrap($field_prefix, $field_identifier)) {
         $code[] = rtrim($field_prefix);
-        $code[] = "  // {$field_identifier}";
+        $code[] = "  // {$field_identifier}.";
       }
       else {
-        $code[] = $field_prefix . $field_identifier;
+        $code[] = $field_prefix . $field_identifier . '.';
       }
       $code[] = "  \$field_bases[{$field_identifier}] = {$field_export};";
       $code[] = "";
@@ -201,10 +201,10 @@ function field_instance_features_export_render($module, $data, $export = NULL) {
       $instance_identifier = features_var_export($identifier);
       if (features_field_export_needs_wrap($instance_prefix, $instance_identifier)) {
         $code[] = rtrim($instance_prefix);
-        $code[] = "  // {$instance_identifier}";
+        $code[] = "  // {$instance_identifier}.";
       }
       else {
-        $code[] = $instance_prefix . $instance_identifier;
+        $code[] = $instance_prefix . $instance_identifier . '.';
       }
       $code[] = "  \$field_instances[{$instance_identifier}] = {$field_export};";
       $code[] = "";
@@ -276,11 +276,21 @@ function field_base_features_rebuild($module) {
         $existing_field = $existing_fields[$field['field_name']];
         $array_diff_result = drupal_array_diff_assoc_recursive($field + $existing_field, $existing_field);
         if (!empty($array_diff_result)) {
-          field_update_field($field);
+          try {
+            field_update_field($field);
+          }
+          catch (FieldException $e) {
+            watchdog('features', 'Attempt to update field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+          }
         }
       }
       else {
-        field_create_field($field);
+        try {
+          field_create_field($field);
+        }
+        catch (FieldException $e) {
+          watchdog('features', 'Attempt to create field %label failed: %message', array('%label' => $field['field_name'], '%message' => $e->getMessage()), WATCHDOG_ERROR);
+        }
         $existing_fields[$field['field_name']] = $field;
       }
       variable_set('menu_rebuild_needed', TRUE);
@@ -410,7 +420,7 @@ function field_features_export_render($module, $data, $export = NULL) {
         unset($field['field_config']['storage']);
       }
       // If we still have a storage declaration here it means that a non-default
-      // storage type was altered into to the field definition. And noone would
+      // storage type was altered into to the field definition. And no one would
       // never need to change the 'details' key, so don't render it.
       if (isset($field['field_config']['storage']['details'])) {
         unset($field['field_config']['storage']['details']);
@@ -562,5 +572,6 @@ function features_field_load($identifier) {
  * @see https://www.drupal.org/node/1354
  */
 function features_field_export_needs_wrap($prefix, $identifier) {
-  return (strlen($prefix) + strlen($identifier) > 80);
+  // Check for 79 characters, since the comment ends with a full stop.
+  return (strlen($prefix) + strlen($identifier) > 79);
 }

+ 2 - 2
sites/all/modules/contrib/admin/features/includes/features.locale.inc

@@ -141,8 +141,8 @@ function _features_language_save($language) {
   }
   // Update Existing language.
   else {
-    // @TODO: get properties from schema.
-    $properties = array('language', 'name', 'native', 'direction', 'enabled', 'plurals', 'formula', 'domain', 'prefix', 'weight', 'javascript');
+    // Get field list from table schema.
+    $properties = drupal_schema_fields_sql('languages');
     // The javascript hash is not in the imported data but should be empty
     if (!isset($language->javascript)) {
       $language->javascript = '';

+ 2 - 2
sites/all/modules/contrib/admin/features/includes/features.menu.inc

@@ -103,7 +103,6 @@ function menu_custom_features_export_render($module, $data) {
     $code[] = features_translatables_export($translatables, '  ');
   }
 
-  $code[] = '';
   $code[] = '  return $menus;';
   $code = implode("\n", $code);
   return array('menu_default_menu_custom' => $code);
@@ -248,7 +247,7 @@ function menu_links_features_export_render($module, $data, $export = NULL) {
        unset($link['plid']);
        unset($link['mlid']);
 
-      $code[] = "  // Exported menu link: {$new_identifier}";
+      $code[] = "  // Exported menu link: {$new_identifier}.";
       $code[] = "  \$menu_links['{$new_identifier}'] = ". features_var_export($link, '  ') .";";
       $translatables[] = $link['link_title'];
     }
@@ -316,6 +315,7 @@ function menu_links_features_rebuild_ordered($menu_links, $reset = FALSE) {
     foreach ($unordered as $link) {
       $identifier = menu_links_features_identifier($link);
       $ordered[$identifier] = 0;
+      $all_links[$identifier] = $link;
     }
     asort($ordered);
   }

+ 9 - 0
sites/all/modules/contrib/admin/features/includes/features.node.inc

@@ -145,11 +145,14 @@ function node_features_disable_feature($module) {
  * When a features module is enabled, modify any node types it provides so
  * they can no longer be deleted manually through the content types UI.
  *
+ * Update the database cache of node types if needed.
+ *
  * @param $module
  *   Name of module that has been enabled.
  */
 function node_features_enable_feature($module) {
   if ($default_types = features_get_default('node', $module)) {
+    $rebuild = FALSE;
     foreach ($default_types as $type_name => $type_info) {
       // Ensure the type exists.
       if ($type_info = node_type_load($type_name)) {
@@ -160,6 +163,12 @@ function node_features_enable_feature($module) {
         $type_info->disabled = 0;
         node_type_save($type_info);
       }
+      else {
+        $rebuild = TRUE;
+      }
+    }
+    if ($rebuild) {
+      node_types_rebuild();
     }
   }
 }

+ 1 - 1
sites/all/modules/contrib/admin/features/tests/features.test

@@ -218,7 +218,7 @@ class FeaturesEnableTestCase extends DrupalWebTestCase {
 
 
 /**
- * Tests intergration of ctools for features.
+ * Tests integration of ctools for features.
  */
 class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
   protected $profile = 'testing';

+ 3 - 3
sites/all/modules/contrib/admin/features/tests/features_test/features_test.info

@@ -21,9 +21,9 @@ features[user_permission][] = create features_test content
 features[views_view][] = features_test
 hidden = 1
 
-; Information added by Drupal.org packaging script on 2015-06-24
-version = "7.x-2.6"
+; Information added by Drupal.org packaging script on 2016-04-18
+version = "7.x-2.10"
 core = "7.x"
 project = "features"
-datestamp = "1435165997"
+datestamp = "1461011641"