updated features
This commit is contained in:
parent
fb0666538c
commit
e2fde76aff
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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 = '';
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user