updated date pathauto addressfield honeypot features modules
This commit is contained in:
@@ -203,7 +203,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
|
||||
$form['advanced']['generate'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Generate feature'),
|
||||
'#submit' => array('features_export_build_form_submit'),
|
||||
'#submit' => array('features_export_build_form_submit', 'features_form_rebuild'),
|
||||
);
|
||||
}
|
||||
// build the Component Listing panel on the right
|
||||
@@ -239,7 +239,7 @@ function features_export_form($form, $form_state, $feature = NULL) {
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Download feature'),
|
||||
'#weight' => 10,
|
||||
'#submit' => array('features_export_build_form_submit'),
|
||||
'#submit' => array('features_export_build_form_submit', 'features_form_rebuild'),
|
||||
);
|
||||
|
||||
$form['#attached']['library'][] = array('system', 'ui.dialog');
|
||||
@@ -597,6 +597,7 @@ function _features_export_build($feature, &$form_state) {
|
||||
$component_export['selected'][$section] = array();
|
||||
}
|
||||
$options = features_invoke($component, 'features_export_options');
|
||||
drupal_alter('features_export_options', $options, $component);
|
||||
if (!empty($options)) {
|
||||
$exported_components = !empty($exported_features_info[$component]) ? $exported_features_info[$component] : array();
|
||||
$new_components = !empty($new_features_info[$component]) ? $new_features_info[$component] : array();
|
||||
@@ -843,7 +844,7 @@ function _features_export_generate($export, $form_state, $feature = NULL) {
|
||||
}
|
||||
// If either update status-related keys are provided, add a project key
|
||||
// corresponding to the module name.
|
||||
if (!empty($form_state['values']['version']) || !empty($form_state['values']['project_status_url'])) {
|
||||
if (!empty($form_state['values']['version']) && !empty($form_state['values']['project_status_url'])) {
|
||||
$export['project'] = $form_state['values']['module_name'];
|
||||
}
|
||||
if (!empty($form_state['values']['version'])) {
|
||||
@@ -900,6 +901,35 @@ function features_export_build_form_submit($form, &$form_state) {
|
||||
|
||||
$tar = array();
|
||||
$filenames = array();
|
||||
// Copy any files if _files key is there.
|
||||
if (!empty($files['_files'])) {
|
||||
foreach ($files['_files'] as $file_name => $file_info) {
|
||||
if ($generate) {
|
||||
// See if files are in a sub directory.
|
||||
if (strpos($file_name, '/')) {
|
||||
$file_directory = $directory . '/' . substr($file_name, 0, strrpos($file_name, '/'));
|
||||
if (!is_dir($file_directory)) {
|
||||
mkdir($file_directory);
|
||||
}
|
||||
}
|
||||
if (!empty($file_info['file_path'])) {
|
||||
file_unmanaged_copy($file_info['file_path'], "{$directory}/{$file_name}", FILE_EXISTS_REPLACE);
|
||||
}
|
||||
elseif ($file_info['file_content']) {
|
||||
file_put_contents("{$directory}/{$file_name}", $file_info['file_content']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!empty($file_info['file_path'])) {
|
||||
print features_tar_create("{$module_name}/{$file_name}", file_get_contents($file_info['file_path']));
|
||||
}
|
||||
elseif ($file_info['file_content']) {
|
||||
features_tar_create("{$directory}/{$file_name}", $file_info['file_content']);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($files['_files']);
|
||||
}
|
||||
foreach ($files as $extension => $file_contents) {
|
||||
if (!in_array($extension, array('module', 'info'))) {
|
||||
$extension .= '.inc';
|
||||
@@ -973,28 +1003,14 @@ function features_filter_hidden($module) {
|
||||
* Form constructor for the features configuration form.
|
||||
*/
|
||||
function features_admin_form($form, $form_state) {
|
||||
$features = _features_get_features_list();
|
||||
$modules = array_filter(features_get_modules(), 'features_filter_hidden');
|
||||
$conflicts = features_get_conflicts();
|
||||
|
||||
// Load export functions to use in comparison.
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
|
||||
// Clear & rebuild key caches
|
||||
features_get_info(NULL, NULL, TRUE);
|
||||
features_rebuild();
|
||||
|
||||
$modules = array_filter(features_get_modules(), 'features_filter_hidden');
|
||||
$features = array_filter(features_get_features(), 'features_filter_hidden');
|
||||
$conflicts = features_get_conflicts();
|
||||
|
||||
foreach ($modules as $key => $module) {
|
||||
if ($module->status && !empty($module->info['dependencies'])) {
|
||||
foreach ($module->info['dependencies'] as $dependent) {
|
||||
if (isset($features[$dependent])) {
|
||||
$features[$dependent]->dependents[$key] = $module->info['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty($features) ) {
|
||||
if (empty($features) ) {
|
||||
$form['no_features'] = array(
|
||||
'#markup' => t('No Features were found. Please use the !create_link link to create
|
||||
a new Feature module, or upload an existing Feature to your modules directory.',
|
||||
@@ -1328,6 +1344,13 @@ function features_form_submit(&$form, &$form_state) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for the 'manage features' form rebuild button.
|
||||
*/
|
||||
function features_form_rebuild() {
|
||||
cache_clear_all('features:features_list', 'cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form for clearing cache after enabling a feature.
|
||||
*/
|
||||
@@ -1588,3 +1611,42 @@ function _features_get_used($module_name = NULL) {
|
||||
$features_ignore_conflicts = $old_value;
|
||||
return $conflicts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the array of features as expected on the Manage Features form.
|
||||
* Uses caching for performance reasons if caching is enabled.
|
||||
*
|
||||
* @internal - This function might return cached result with outdated data,
|
||||
* use with caution.
|
||||
*/
|
||||
function _features_get_features_list() {
|
||||
$features = array();
|
||||
|
||||
$cache = cache_get('features:features_list');
|
||||
if ($cache) {
|
||||
$features = $cache->data;
|
||||
}
|
||||
|
||||
if (empty($features)) {
|
||||
// Clear & rebuild key caches
|
||||
features_get_info(NULL, NULL, TRUE);
|
||||
features_rebuild();
|
||||
|
||||
$modules = array_filter(features_get_modules(), 'features_filter_hidden');
|
||||
$features = array_filter(features_get_features(), 'features_filter_hidden');
|
||||
|
||||
foreach ($modules as $key => $module) {
|
||||
if ($module->status && !empty($module->info['dependencies'])) {
|
||||
foreach ($module->info['dependencies'] as $dependent) {
|
||||
if (isset($features[$dependent])) {
|
||||
$features[$dependent]->dependents[$key] = $module->info['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache_set('features:features_list', $features);
|
||||
}
|
||||
|
||||
return $features;
|
||||
}
|
||||
|
@@ -157,7 +157,8 @@ function hook_features_export_options() {
|
||||
* of the module, e.g. the key for `hook_example` should simply be `example`
|
||||
* The values in the array can also be in the form of an associative array
|
||||
* with the required key of 'code' and optional key of 'args', if 'args' need
|
||||
* to be added to the hook.
|
||||
* to be added to the hook. Alternate it can be an associative array in the
|
||||
* same style as hook_features_export_files() to add additional files.
|
||||
*/
|
||||
function hook_features_export_render($module_name, $data, $export = NULL) {
|
||||
$code = array();
|
||||
@@ -314,6 +315,26 @@ function hook_features_pipe_alter(&$pipe, $data, $export) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add extra files to the exported file.
|
||||
*
|
||||
* @return array
|
||||
* An array of files, keyed by file name that will appear in feature and
|
||||
* with either file_path key to indicate where to copy the file from or
|
||||
* file_content key to indicate the contents of the file.
|
||||
*/
|
||||
function hook_features_export_files($module_name, $export) {
|
||||
return array('css/main.css' => array('file_content' => 'body {background-color:blue;}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the extra files added to the export.
|
||||
*/
|
||||
function hook_features_export_files_alter(&$files, $module_name, $export) {
|
||||
$files['css/main.css']['file_content'] = 'body {background-color:black;}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup features_component_alter_hooks Feature's component alter hooks
|
||||
* @{
|
||||
|
@@ -610,6 +610,28 @@ function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
|
||||
drupal_flush_all_caches();
|
||||
$export = _drush_features_generate_export($info, $module_name);
|
||||
$files = features_export_render($export, $module_name, TRUE);
|
||||
// Copy any files if _files key is there.
|
||||
if (!empty($files['_files'])) {
|
||||
foreach ($files['_files'] as $file_name => $file_info) {
|
||||
// See if files are in a sub directory.
|
||||
if (strpos($file_name, '/')) {
|
||||
$file_directory = $directory . '/' . substr($file_name, 0, strrpos($file_name, '/'));
|
||||
if (!is_dir($file_directory)) {
|
||||
drush_op('mkdir', $file_directory);
|
||||
}
|
||||
}
|
||||
if (!empty($file_info['file_path'])) {
|
||||
drush_op('file_unmanaged_copy', $file_info['file_path'], "{$directory}/{$file_name}", FILE_EXISTS_REPLACE);
|
||||
}
|
||||
elseif (!empty($file_info['file_content'])) {
|
||||
drush_op('file_put_contents', "{$directory}/{$file_name}", $file_info['file_content']);
|
||||
}
|
||||
else {
|
||||
drush_log(dt("Entry for @file_name.in !module is invalid. ", array('!module' => $module_name, '@file_name' => $file_name)), 'ok');
|
||||
}
|
||||
}
|
||||
unset($files['_files']);
|
||||
}
|
||||
foreach ($files as $extension => $file_contents) {
|
||||
if (!in_array($extension, array('module', 'info'))) {
|
||||
$extension .= '.inc';
|
||||
|
@@ -45,6 +45,9 @@ function features_populate($info, $module_name) {
|
||||
* @return fully populated $export array.
|
||||
*/
|
||||
function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
|
||||
// Ensure that the export will be created in the english language.
|
||||
_features_set_export_language();
|
||||
|
||||
if ($reset) {
|
||||
drupal_static_reset(__FUNCTION__);
|
||||
}
|
||||
@@ -295,6 +298,11 @@ function features_export_render($export, $module_name, $reset = FALSE) {
|
||||
}
|
||||
|
||||
foreach ($hooks as $hook_name => $hook_info) {
|
||||
// These are purely files that will be copied over.
|
||||
if (is_array($hook_info) && (!empty($hook_info['file_path']) || !empty($hook_info['file_content']))) {
|
||||
$code['_files'][$hook_name] = $hook_info;
|
||||
continue;
|
||||
}
|
||||
$hook_code = is_array($hook_info) ? $hook_info['code'] : $hook_info;
|
||||
$hook_args = is_array($hook_info) && !empty($hook_info['args']) ? $hook_info['args'] : '';
|
||||
$hook_file = is_array($hook_info) && !empty($hook_info['file']) ? $hook_info['file'] : $file['name'];
|
||||
@@ -305,7 +313,17 @@ function features_export_render($export, $module_name, $reset = FALSE) {
|
||||
// Finalize strings to be written to files
|
||||
$code = array_filter($code);
|
||||
foreach ($code as $filename => $contents) {
|
||||
$code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
|
||||
if ($filename != '_files') {
|
||||
$code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Allow extra files be added to feature.
|
||||
if ($files = module_invoke_all('features_export_files', $module_name, $export)) {
|
||||
$code['_files'] = !empty($code['_files']) ? $code['_files'] + $files : $files;
|
||||
}
|
||||
if (!empty($code['_files'])) {
|
||||
drupal_alter('features_export_files', $code['_files'], $module_name, $export);
|
||||
}
|
||||
|
||||
// Generate info file output
|
||||
@@ -394,8 +412,8 @@ function features_detect_overrides($module) {
|
||||
$overridden = array();
|
||||
|
||||
// Compare feature info
|
||||
_features_sanitize($module->info);
|
||||
_features_sanitize($export);
|
||||
features_sanitize($module->info);
|
||||
features_sanitize($export);
|
||||
|
||||
$compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info));
|
||||
if ($compare['normal'] !== $compare['default']) {
|
||||
@@ -408,8 +426,8 @@ function features_detect_overrides($module) {
|
||||
if ($state != FEATURES_DEFAULT) {
|
||||
$normal = features_get_normal($component, $module->name);
|
||||
$default = features_get_default($component, $module->name);
|
||||
_features_sanitize($normal);
|
||||
_features_sanitize($default);
|
||||
features_sanitize($normal, $component);
|
||||
features_sanitize($default, $component);
|
||||
|
||||
$compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default));
|
||||
if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) {
|
||||
@@ -663,8 +681,7 @@ function features_get_signature($state = 'default', $module_name, $component, $r
|
||||
break;
|
||||
}
|
||||
if (!empty($objects)) {
|
||||
$objects = (array) $objects;
|
||||
_features_sanitize($objects);
|
||||
features_sanitize($objects, $component);
|
||||
return md5(_features_linetrim(features_var_export($objects)));
|
||||
}
|
||||
return FALSE;
|
||||
@@ -721,7 +738,7 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
|
||||
|
||||
// Special handling for dependencies component.
|
||||
if ($component === 'dependencies') {
|
||||
$cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], 'module_exists') : array();
|
||||
$cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], '_features_module_exists') : array();
|
||||
}
|
||||
// All other components.
|
||||
else {
|
||||
@@ -739,6 +756,17 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
|
||||
return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to determine if a module is enabled
|
||||
* @param $module
|
||||
* This module name comes from the .info file and can have version info in it.
|
||||
*/
|
||||
function _features_module_exists($module) {
|
||||
$parsed_dependency = drupal_parse_dependency($module);
|
||||
$name = $parsed_dependency['name'];
|
||||
return module_exists($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get defaults for a given module/component pair.
|
||||
*/
|
||||
@@ -970,25 +998,52 @@ function _features_linetrim($code) {
|
||||
return implode("\n", $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to "sanitize" an array or object.
|
||||
* Converts everything to an array, sorts the keys, removes recursion.
|
||||
* @param $array
|
||||
* @param $component string name of component
|
||||
* @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));
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Sanitizes" an array recursively, performing two key operations:
|
||||
* - Sort an array by its keys (assoc) or values (non-assoc)
|
||||
* - Remove any null or empty values for associative arrays (array_filter()).
|
||||
* @param bool $remove_empty if set, remove null or empty values for assoc arrays.
|
||||
*/
|
||||
function _features_sanitize(&$array) {
|
||||
function _features_sanitize(&$array, $remove_empty = TRUE) {
|
||||
if (is_object($array)) {
|
||||
$array = get_object_vars($array);
|
||||
}
|
||||
if (is_array($array)) {
|
||||
$is_assoc = _features_is_assoc($array);
|
||||
if ($is_assoc) {
|
||||
ksort($array, SORT_STRING);
|
||||
$array = array_filter($array);
|
||||
if ($remove_empty) {
|
||||
$array = array_filter($array);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sort($array);
|
||||
}
|
||||
foreach ($array as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
if (is_array($v) or is_object($v)) {
|
||||
_features_sanitize($array[$k]);
|
||||
if ($is_assoc && empty($array[$k])) {
|
||||
if ($remove_empty && $is_assoc && empty($array[$k])) {
|
||||
unset($array[$k]);
|
||||
}
|
||||
}
|
||||
@@ -1010,3 +1065,127 @@ function _features_sanitize(&$array) {
|
||||
function _features_is_assoc($array) {
|
||||
return (is_array($array) && (0 !== count(array_diff_key($array, array_keys(array_keys($array)))) || count($array)==0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
*/
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($subobject);
|
||||
}
|
||||
else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to removes a set of keys an object/array.
|
||||
*
|
||||
* @param $item
|
||||
* An object or array passed by reference.
|
||||
* @param $ignore_keys
|
||||
* Array of keys to be ignored. Values are the level of the key.
|
||||
* @param $level
|
||||
* Level of key to remove. Up to 2 levels deep because $item can still be
|
||||
* recursive
|
||||
*/
|
||||
function _features_remove_ignores(&$item, $ignore_keys, $level = -1) {
|
||||
$is_object = is_object($item);
|
||||
if (!is_array($item) && !is_object($item)) {
|
||||
return;
|
||||
}
|
||||
foreach ($item as $key => $value) {
|
||||
if (isset($ignore_keys[$key]) && ($ignore_keys[$key] == $level)) {
|
||||
if ($is_object) {
|
||||
unset($item->$key);
|
||||
}
|
||||
else {
|
||||
unset($item[$key]);
|
||||
}
|
||||
}
|
||||
elseif (($level < 2) && (is_array($value) || is_object($value))) {
|
||||
_features_remove_ignores($value, $ignore_keys, $level+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of keys to be ignored for various exportables
|
||||
* @param $component
|
||||
* The component to retrieve ignore_keys from.
|
||||
*/
|
||||
function _features_get_ignore_keys($component) {
|
||||
static $cache;
|
||||
if (!isset($cache[$component])) {
|
||||
$cache[$component] = module_invoke_all('features_ignore', $component);
|
||||
}
|
||||
return $cache[$component];
|
||||
}
|
||||
|
@@ -6,9 +6,9 @@ files[] = tests/features.test
|
||||
|
||||
configure = admin/structure/features/settings
|
||||
|
||||
; Information added by Drupal.org packaging script on 2015-04-13
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2015-06-24
|
||||
version = "7.x-2.6"
|
||||
core = "7.x"
|
||||
project = "features"
|
||||
datestamp = "1428944073"
|
||||
datestamp = "1435165997"
|
||||
|
||||
|
@@ -5,6 +5,15 @@
|
||||
* Install, update and uninstall functions for the features module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function features_schema() {
|
||||
$schema['cache_features'] = drupal_get_schema_unprocessed('system', 'cache');
|
||||
$schema['cache_features']['description'] = 'Cache table for features to store module info.';
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
@@ -33,7 +42,7 @@ function features_uninstall() {
|
||||
->execute();
|
||||
db_delete('variable')
|
||||
->condition('name', 'features_component_locked_%', 'LIKE')
|
||||
->execute();variable_del('features_component_locked_' . $component);
|
||||
->execute();
|
||||
|
||||
if (db_table_exists('menu_custom')) {
|
||||
db_delete('menu_custom')
|
||||
@@ -130,3 +139,13 @@ function features_update_6101() {
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {cache_features} table.
|
||||
*/
|
||||
function features_update_7200() {
|
||||
if (!db_table_exists('cache_features')) {
|
||||
$schema = drupal_get_schema_unprocessed('system', 'cache');
|
||||
db_create_table('cache_features', $schema);
|
||||
}
|
||||
}
|
||||
|
@@ -128,10 +128,12 @@ jQuery.fn.sortElements = (function(){
|
||||
if (!$(this).hasClass('features-checkall')) {
|
||||
var key = $(this).attr('name');
|
||||
var matches = key.match(/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/);
|
||||
var component = matches[1];
|
||||
var item = matches[4];
|
||||
if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
|
||||
$(this).parent().addClass('features-conflict');
|
||||
if (matches != null) {
|
||||
var component = matches[1];
|
||||
var item = matches[4];
|
||||
if ((component in moduleConflicts) && (moduleConflicts[component].indexOf(item) != -1)) {
|
||||
$(this).parent().addClass('features-conflict');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -290,7 +292,7 @@ jQuery.fn.sortElements = (function(){
|
||||
}
|
||||
|
||||
// Handle component selection UI
|
||||
$('#features-export-wrapper input[type=checkbox]', context).click(function() {
|
||||
$('#features-export-wrapper input[type=checkbox]:not(.processed)', context).addClass('processed').click(function() {
|
||||
_resetTimeout();
|
||||
if ($(this).hasClass('component-select')) {
|
||||
moveCheckbox(this, 'added', true);
|
||||
|
@@ -268,14 +268,17 @@ function features_theme() {
|
||||
* Implements hook_flush_caches().
|
||||
*/
|
||||
function features_flush_caches() {
|
||||
if (variable_get('features_rebuild_on_flush', TRUE)) {
|
||||
if (($modules_changed = variable_get('features_modules_changed', FALSE)) || variable_get('features_rebuild_on_flush', TRUE)) {
|
||||
if ($modules_changed) {
|
||||
variable_set('features_modules_changed', FALSE);
|
||||
}
|
||||
features_rebuild();
|
||||
// Don't flush the modules cache during installation, for performance reasons.
|
||||
if (variable_get('install_task') == 'done') {
|
||||
features_get_modules(NULL, TRUE);
|
||||
}
|
||||
}
|
||||
return array();
|
||||
return array('cache_features');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -349,6 +352,15 @@ function features_modules_disabled($modules) {
|
||||
* Implements hook_modules_enabled().
|
||||
*/
|
||||
function features_modules_enabled($modules) {
|
||||
// Allow distributions to disable this behavior and rebuild the features
|
||||
// manually inside a batch.
|
||||
if (!variable_get('features_rebuild_on_module_install', TRUE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// mark modules as being changed for test in features_flush_caches
|
||||
variable_set('features_modules_changed', TRUE);
|
||||
|
||||
// Go through all modules and gather features that can be enabled.
|
||||
$items = array();
|
||||
foreach ($modules as $module) {
|
||||
@@ -385,7 +397,7 @@ function features_include($reset = FALSE) {
|
||||
// Features provides integration on behalf of these modules.
|
||||
// The features include provides handling for the feature dependencies.
|
||||
// Note that ctools is placed last because it implements hooks "dynamically" for other modules.
|
||||
$modules = array('features', 'block', 'context', 'field', 'filter', 'image', 'locale', 'menu', 'node', 'taxonomy', 'user', 'views', 'ctools');
|
||||
$modules = array('features', 'block', 'contact', 'context', 'field', 'filter', 'image', 'locale', 'menu', 'node', 'taxonomy', 'user', 'views', 'ctools');
|
||||
|
||||
foreach (array_filter($modules, 'module_exists') as $module) {
|
||||
module_load_include('inc', 'features', "includes/features.$module");
|
||||
@@ -535,13 +547,13 @@ function features_get_components($component = NULL, $key = NULL, $reset = FALSE)
|
||||
|
||||
if ($reset || !isset($components) || !isset($component_by_key)) {
|
||||
$components = $component_by_key = array();
|
||||
if (!$reset && ($cache = cache_get('features_api'))) {
|
||||
if (!$reset && ($cache = cache_get('features_api', 'cache_features'))) {
|
||||
$components = $cache->data;
|
||||
}
|
||||
else {
|
||||
$components = module_invoke_all('features_api');
|
||||
drupal_alter('features_api', $components);
|
||||
cache_set('features_api', $components);
|
||||
cache_set('features_api', $components, 'cache_features');
|
||||
}
|
||||
|
||||
foreach ($components as $component_type => $component_information) {
|
||||
@@ -607,6 +619,8 @@ function features_hook($component, $hook, $reset = FALSE) {
|
||||
* Clear the module info cache.
|
||||
*/
|
||||
function features_install_modules($modules) {
|
||||
variable_set('features_modules_changed', TRUE);
|
||||
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
$files = system_rebuild_module_data();
|
||||
|
||||
@@ -650,7 +664,7 @@ function features_get_features($name = NULL, $reset = FALSE) {
|
||||
function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
|
||||
static $cache;
|
||||
if (!isset($cache)) {
|
||||
$cache = cache_get('features_module_info');
|
||||
$cache = cache_get('features_module_info', 'cache_features');
|
||||
}
|
||||
if (empty($cache) || $reset) {
|
||||
$data = array(
|
||||
@@ -738,7 +752,7 @@ function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
|
||||
$data['feature'] = $sorted;
|
||||
|
||||
variable_set('features_ignored_orphans', $ignored);
|
||||
cache_set("features_module_info", $data);
|
||||
cache_set('features_module_info', $data, 'cache_features');
|
||||
$cache = new stdClass();
|
||||
$cache->data = $data;
|
||||
}
|
||||
@@ -1067,6 +1081,7 @@ function features_hook_info() {
|
||||
'features_api',
|
||||
'features_pipe_alter',
|
||||
'features_export_alter',
|
||||
'features_export_options_alter',
|
||||
);
|
||||
return array_fill_keys($hooks, array('group' => 'features'));
|
||||
}
|
||||
@@ -1189,7 +1204,6 @@ function features_feature_lock($feature, $component = NULL) {
|
||||
variable_set('features_feature_locked', $locked);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unlocks a feature or it's component.
|
||||
*/
|
||||
@@ -1203,3 +1217,115 @@ function features_feature_unlock($feature, $component = NULL) {
|
||||
}
|
||||
variable_set('features_feature_locked', $locked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets 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__)) {
|
||||
// Create the language object as language_default() does.
|
||||
$GLOBALS['language'] = (object) array(
|
||||
'language' => 'en',
|
||||
'name' => 'English',
|
||||
'native' => 'English',
|
||||
'direction' => 0,
|
||||
'enabled' => 1,
|
||||
'plurals' => 0,
|
||||
'formula' => '',
|
||||
'domain' => '',
|
||||
'prefix' => '',
|
||||
'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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_features_ignore().
|
||||
*/
|
||||
function features_features_ignore($component) {
|
||||
// Determine which keys need to be ignored for override diff for various components.
|
||||
// Value is how many levels deep the key is.
|
||||
$ignores = array();
|
||||
switch ($component) {
|
||||
case 'views_view':
|
||||
$ignores['current_display'] = 0;
|
||||
$ignores['display_handler'] = 0;
|
||||
$ignores['handler'] = 2;
|
||||
$ignores['query'] = 0;
|
||||
$ignores['localization_plugin'] = 0;
|
||||
// Views automatically adds these two on export to set values.
|
||||
$ignores['api_version'] = 0;
|
||||
$ignores['disabled'] = 0;
|
||||
break;
|
||||
case 'image':
|
||||
$ignores['module'] = 0;
|
||||
$ignores['name'] = 0;
|
||||
$ignores['storage'] = 0;
|
||||
// Various properties are loaded into the effect in image_styles.
|
||||
$ignores['summary theme'] = 2;
|
||||
$ignores['module'] = 2;
|
||||
$ignores['label'] = 2;
|
||||
$ignores['help'] = 2;
|
||||
$ignores['form callback'] = 2;
|
||||
$ignores['effect callback'] = 2;
|
||||
$ignores['dimensions callback'] = 2;
|
||||
break;
|
||||
case 'field':
|
||||
$ignores['locked'] = 1;
|
||||
break;
|
||||
}
|
||||
return $ignores;
|
||||
}
|
||||
|
@@ -274,7 +274,8 @@ function field_base_features_rebuild($module) {
|
||||
// Create or update field.
|
||||
if (isset($existing_fields[$field['field_name']])) {
|
||||
$existing_field = $existing_fields[$field['field_name']];
|
||||
if ($field + $existing_field !== $existing_field) {
|
||||
$array_diff_result = drupal_array_diff_assoc_recursive($field + $existing_field, $existing_field);
|
||||
if (!empty($array_diff_result)) {
|
||||
field_update_field($field);
|
||||
}
|
||||
}
|
||||
@@ -483,7 +484,8 @@ function field_features_rebuild($module) {
|
||||
$field_config = $field['field_config'];
|
||||
if (isset($existing_fields[$field_config['field_name']])) {
|
||||
$existing_field = $existing_fields[$field_config['field_name']];
|
||||
if ($field_config + $existing_field !== $existing_field) {
|
||||
$array_diff_result = drupal_array_diff_assoc_recursive($field_config + $existing_field, $existing_field);
|
||||
if (!empty($array_diff_result)) {
|
||||
try {
|
||||
field_update_field($field_config);
|
||||
}
|
||||
|
@@ -253,11 +253,11 @@ function menu_links_features_export_render($module, $data, $export = NULL) {
|
||||
$translatables[] = $link['link_title'];
|
||||
}
|
||||
}
|
||||
$code[] = '';
|
||||
if (!empty($translatables)) {
|
||||
$code[] = features_translatables_export($translatables, ' ');
|
||||
}
|
||||
|
||||
$code[] = '';
|
||||
$code[] = ' return $menu_links;';
|
||||
$code = implode("\n", $code);
|
||||
return array('menu_default_menu_links' => $code);
|
||||
|
@@ -287,3 +287,43 @@ class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test detecting modules as features.
|
||||
*/
|
||||
class FeaturesDetectionTestCase extends DrupalWebTestCase {
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Test info.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Feature Detection tests'),
|
||||
'description' => t('Run tests for detecting items as features.') ,
|
||||
'group' => t('Features'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array(
|
||||
'features',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run test.
|
||||
*/
|
||||
public function test() {
|
||||
module_load_include('inc', 'features', 'features.export');
|
||||
// First test that features_populate inserts the features api key.
|
||||
$export = features_populate(array(), array(), 'features_test_empty_fake');
|
||||
$this->assertTrue(!empty($export['features']['features_api']) && key($export['features']['features_api']) == 'api:' . FEATURES_API, 'Features API key added to new export.');
|
||||
$this->assertTrue((bool)features_get_features('features_test'), 'Features test recognized as a feature.');
|
||||
$this->assertFalse((bool)features_get_features('features'), 'Features module not recognized as a feature.');
|
||||
}
|
||||
}
|
||||
|
@@ -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-04-13
|
||||
version = "7.x-2.5"
|
||||
; Information added by Drupal.org packaging script on 2015-06-24
|
||||
version = "7.x-2.6"
|
||||
core = "7.x"
|
||||
project = "features"
|
||||
datestamp = "1428944073"
|
||||
datestamp = "1435165997"
|
||||
|
||||
|
Reference in New Issue
Block a user