non security modules update

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-20 16:32:07 +02:00
parent 6a8d30db08
commit 37fbabab56
466 changed files with 32690 additions and 9652 deletions

View File

@@ -19,16 +19,39 @@ function features_settings_form($form, $form_state) {
'#title' => t('Show components on create/edit feature form.'),
'#description' => t('Components with no options will not be shown no matter the setting below. Disabled components cannot be used with admin form.')
);
foreach ($components as $compontent => $info) {
$form['lock_components'] = array(
'#type' => 'fieldset',
'#title' => t('Lock components'),
'#description' => t('Locked components will be prevented from ever being reverted. For example, if site builder updates a feature with new settings for a field instance, but field instance is locked, it will not update that field. If the item is purely in code, like a view, the view changed when the code is updated no matter these settings.')
);
$form['features_lock_mode'] = array(
'#type' => 'radios',
'#title' => t('Features lock mode'),
'#options' => array(
'rebuild' => t('Allow rebuild (prevent revert)'),
'all' => t('Prevent rebuild and revert'),
),
'#description' => t('Rebuild will allow the feature to be updated till the point features has detected that the item has changed deliberately on the site, e.g. is overriden.'),
'#default_value' => variable_get('features_lock_mode', 'all'),
);
foreach ($components as $component => $info) {
if (empty($info['feature_source']) && empty($info['features_source'])) {
continue;
}
$form['show_components']['features_admin_show_component_' . $compontent] = array(
'#title' => t('@name (@machine)', array('@name' => $info['name'], '@machine' => $compontent)),
$form['show_components']['features_admin_show_component_' . $component] = array(
'#title' => t('@name (@machine)', array('@name' => $info['name'], '@machine' => $component)),
'#type' => 'checkbox',
'#default_value' => variable_get('features_admin_show_component_' . $compontent, TRUE),
'#default_value' => variable_get('features_admin_show_component_' . $component, TRUE),
);
if ($compontent == 'menu_links' && ($menus = menu_get_menus())) {
if (features_hook($component, 'features_revert') || features_hook($component, 'features_rebuild')) {
$form['lock_components']['features_component_locked_' . $component] = array(
'#title' => t('@name (@machine)', array('@name' => $info['name'], '@machine' => $component)),
'#type' => 'checkbox',
'#default_value' => variable_get('features_component_locked_' . $component, FALSE),
);
}
if ($component == 'menu_links' && ($menus = menu_get_menus())) {
$form['show_components']['features_admin_menu_links'] = array(
'#title' => t('Advanced Menu Link Settings'),
'#type' => 'fieldset',
@@ -104,23 +127,24 @@ function features_export_form($form, $form_state, $feature = NULL) {
'#description' => t('Example: Image gallery') . ' (' . t('Do not begin name with numbers.') . ')',
'#type' => 'textfield',
'#default_value' => !empty($feature->info['name']) ? $feature->info['name'] : '',
'#attributes' => array('class' => array('feature-name')),
);
$form['info']['module_name'] = array(
'#type' => 'textfield',
'#type' => 'machine_name',
'#title' => t('Machine-readable name'),
'#description' => t('Example: image_gallery') . '<br/>' . t('May only contain lowercase letters, numbers and underscores. <strong>Try to avoid conflicts with the names of existing Drupal projects.</strong>'),
'#required' => TRUE,
'#default_value' => $feature_name,
'#attributes' => array('class' => array('feature-module-name')),
'#element_validate' => array('features_export_form_validate_field'),
'#machine_name' => array(
'exists' => 'features_export_form_module_name_exists',
'source' => array('info', 'name'),
),
);
// If recreating this feature, disable machine name field and blank out
// js-attachment classes to ensure the machine name cannot be changed.
if (isset($feature)) {
// If recreating this feature, disable machine name field to ensure the
// machine name cannot be changed, unless user role has granted permission to
// edit machine name of disabled features.
if (isset($feature) && ($feature->status || !user_access('rename features'))) {
$form['info']['module_name']['#value'] = $feature_name;
$form['info']['module_name']['#disabled'] = TRUE;
$form['info']['name']['#attributes'] = array();
}
$form['info']['description'] = array(
'#title' => t('Description'),
@@ -223,6 +247,13 @@ function features_export_form($form, $form_state, $feature = NULL) {
return $form;
}
/**
* Machine name existence callback for the module name.
*/
function features_export_form_module_name_exists($value) {
return (bool) features_get_info('module', $value);
}
/**
* Return the render array elements for the Components selection on the Export form
* @param array $feature - feature associative array
@@ -779,16 +810,6 @@ function features_info_file_preview($form, &$form_state){
*/
function features_export_form_validate_field($element, &$form_state) {
switch ($element['#name']) {
case 'module_name':
if (!preg_match('!^[a-z0-9_]+$!', $element['#value'])) {
form_error($element, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
}
// If user is filling out the feature name for the first time and uses
// the name of an existing module throw an error.
else if (empty($element['#default_value']) && features_get_info('module', $element['#value'])) {
form_error($element, t('A module by the name @name already exists on your site. Please choose a different name.', array('@name' => $element['#value'])));
}
break;
case 'project_status_url':
if (!empty($element['#value']) && !valid_url($element['#value'])) {
form_error($element, t('The URL %url is invalid. Please enter a fully-qualified URL, such as http://www.example.com/feed.xml.', array('%url' => $element['#value'])));
@@ -988,7 +1009,7 @@ function features_admin_form($form, $form_state) {
ksort($features);
foreach ($features as $name => $module) {
$package_title = !empty($module->info['package']) ? $module->info['package'] : t('Other');
$package = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $package_title));
$package = 'package_' . strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $package_title));
// Set up package elements
if (!isset($form[$package])) {
@@ -1052,6 +1073,7 @@ function features_admin_form($form, $form_state) {
}
$href = "admin/structure/features/{$name}";
$href_overridden = module_exists('diff') ? $href . '/diff' : $href;
$module_name = (user_access('administer features')) ? l($module->info['name'], $href) : $module->info['name'];
$form[$package]['status'][$name] = array(
'#type' => 'checkbox',
@@ -1081,7 +1103,7 @@ function features_admin_form($form, $form_state) {
$state .= l(t('Check'), "admin/structure/features/{$name}/status", array('attributes' => array('class' => array('admin-check'))));
$state .= theme('features_storage_link', array('storage' => FEATURES_REBUILDING, 'path' => $href));
$state .= theme('features_storage_link', array('storage' => FEATURES_NEEDS_REVIEW, 'path' => $href));
$state .= theme('features_storage_link', array('storage' => FEATURES_OVERRIDDEN, 'path' => $href));
$state .= theme('features_storage_link', array('storage' => FEATURES_OVERRIDDEN, 'path' => $href_overridden));
$state .= theme('features_storage_link', array('storage' => FEATURES_DEFAULT, 'path' => $href));
}
elseif (!empty($conflicts[$name])) {
@@ -1131,7 +1153,7 @@ function features_admin_components($form, $form_state, $feature) {
drupal_set_breadcrumb($breadcrumb);
module_load_include('inc', 'features', 'features.export');
$form = array();
$form['#feature'] = $feature;
// Store feature info for theme layer.
$form['module'] = array('#type' => 'value', '#value' => $feature->name);
@@ -1195,8 +1217,14 @@ function features_admin_components($form, $form_state, $feature) {
else if (array_key_exists($component, $conflicts)) {
$storage = FEATURES_CONFLICT;
}
// This can be removed if the css is fixed so link doesn't move when
// ajaxing and linke moved.
$lock_link = '<span class="features-lock-empty"></span>';
if (user_access('administer features') && (features_hook($component, 'features_revert') || features_hook($component, 'features_rebuild'))) {
$lock_link = ' ' . theme('features_lock_link', array('feature' => $feature->name, 'component' => $component));
}
$form['components'][$component] = array(
'#markup' => theme('features_storage_link', array('storage' => $storage, 'path' => $path)),
'#markup' => $lock_link . theme('features_storage_link', array('storage' => $storage, 'path' => $path)),
);
}
@@ -1367,6 +1395,71 @@ function features_feature_diff($feature, $component = NULL) {
return $output;
}
/**
* Page callback to lock a component.
*
* @param $feature
* Loaded feature object to be processed for component locking.
* @param $component
* (optional) A specific component to lock.
*
* @return
* Themed display of what is different.
*/
function features_admin_lock($feature, $type = 'ajax', $component = NULL) {
if ($type == 'ajax' && !empty($_GET['token']) && drupal_valid_token($_GET['token'], 'features/' . $feature->name . '/' . ($component ? $component : '')) == $_GET['token']) {
if (features_feature_is_locked($feature->name, $component, FALSE)) {
features_feature_unlock($feature->name, $component);
}
else {
features_feature_lock($feature->name, $component);
}
$commands = array();
$new_link = theme('features_lock_link', array('feature' => $feature->name, 'component' => $component));
$commands[] = ajax_command_replace('#features-lock-link-' . $feature->name . ($component ? '-' . $component : ''), $new_link);
$page = array('#type' => 'ajax', '#commands' => $commands);
ajax_deliver($page);
}
else {
return drupal_get_form('features_feature_lock_confirm_form', $feature, $component);
}
}
/**
* Confirm form for locking a feature.
*/
function features_feature_lock_confirm_form($form, $form_state, $feature, $component) {
$form['#feature'] = $feature;
$form['#component'] = $component;
$is_locked = features_feature_is_locked($feature->name, $component, FALSE);
$args = array(
'@name' => $feature->name,
'@component' => $component ? $component : t('all'),
'!action' => $is_locked ? t('unlock') : t('lock'),
);
$question = t('Are you sure you want to !action this Feature @name (component @component)?', $args);
return confirm_form($form, $question, 'admin/structure/features/' . $feature->name);
}
/**
* Submit callback to lock components of a feature.
*/
function features_feature_lock_confirm_form_submit($form, &$form_state) {
$feature = $form['#feature']->name;
$component = $form['#component'];
if (features_feature_is_locked($feature, $component, FALSE)) {
features_feature_unlock($feature, $component);
drupal_set_message(t('Feature @name (component @component) has been unlocked.', array('@name' => $feature, '@component' => $component ? $component : t('all'))));
}
else {
features_feature_lock($feature, $component);
drupal_set_message(t('Feature @name (component @component) has been locked.', array('@name' => $feature, '@component' => $component ? $component : t('all'))));
}
$form_state['redirect'] = 'admin/structure/features/' . $feature;
}
/**
* Compare the component names. Used to sort alphabetically.
*/

View File

@@ -219,6 +219,42 @@ function hook_features_rebuild($module_name) {
}
}
/**
* Invoked before a restore operation is run.
*
* This hook is called before any of the restore operations on the components is
* run.
*
* @param string $op
* The operation that is triggered: revert, rebuild, disable, enable
* @param array $items
* The items handled by the operation.
*/
function hook_features_pre_restore($op, $items) {
if ($op == 'rebuild') {
// Use features rebuild to rebuild the features independent exports too.
entity_defaults_rebuild();
}
}
/**
* Invoked after a restore operation is run.
*
* This hook is called after any of the restore operations on the components is
* run.
*
* @param string $op
* The operation that is triggered: revert, rebuild, disable, enable
* @param array $items
* The items handled by the operation.
*/
function hook_features_post_restore($op, $items) {
if ($op == 'rebuild') {
// Use features rebuild to rebuild the features independent exports too.
entity_defaults_rebuild();
}
}
/**
* Alter the final array of Component names to be exported, just prior to
* the rendering of defaults. Allows modules a final say in whether or not
@@ -293,6 +329,8 @@ function hook_features_pipe_alter(&$pipe, $data, $export) {
*/
/**
* Deprecated as of 7.x-2.0.
*
* Alter the default fields right before they are cached into the database.
*
* @param &$fields
@@ -301,6 +339,24 @@ function hook_features_pipe_alter(&$pipe, $data, $export) {
function hook_field_default_fields_alter(&$fields) {
}
/**
* Alter the base fields right before they are cached into the database.
*
* @param &$fields
* By reference. The fields that have been declared by another feature.
*/
function hook_field_default_field_bases_alter(&$fields) {
}
/**
* Alter the field instances right before they are cached into the database.
*
* @param &$fields
* By reference. The fields that have been declared by another feature.
*/
function hook_field_default_field_instances_alter(&$fields) {
}
/**
* Alter the default fieldgroup groups right before they are cached into the
* database.

View File

@@ -563,4 +563,17 @@ input.form-submit.features-refresh-button {
fieldset.features-export-component .fieldset-title .component-count {
font-size: 12px;
font-weight: bold;
}
.features-lock-icon {
display: inline-block;
}
.features-components h3 {
display: inline-block;
}
.features-lock-empty {
display: inline-block;
width: 16px;
}

View File

@@ -43,6 +43,7 @@ function features_drush_command() {
'destination' => "Destination path (from Drupal root) of the exported feature. Defaults to '" . $path . "'.",
'version-set' => "Specify a version number for the feature.",
'version-increment' => "Increment the feature's version number.",
'ignore-conflicts' => "Ignore conflicts and export all components.",
),
'drupal dependencies' => array('features'),
'aliases' => array('fe'),
@@ -72,6 +73,9 @@ function features_drush_command() {
'not-exported' => array(
'description' => 'Show only components that have not been exported.',
),
'info-style' => array(
'description' => 'Export components in format suitable for using in an info file.',
),
),
'aliases' => array('fc'),
);
@@ -134,6 +138,18 @@ function features_drush_command() {
'aliases' => array('fd'),
);
$items['features-diff-all'] = array(
'description' => "Show the code difference for all enabled features not in their default state.",
'arguments' => array(
'feature_exclude' => 'A space-delimited list of features to exclude from being reverted.',
),
'options' => array(
'force' => "Bypass the confirmations. This is useful if you want to output all of the diffs to a log file.",
),
'drupal dependencies' => array('features', 'diff'),
'aliases' => array('fda'),
);
return $items;
}
@@ -232,6 +248,7 @@ function drush_features_list() {
function drush_features_components() {
$args = func_get_args();
$components = _drush_features_component_list();
ksort($components);
// If no args supplied, prompt with a list.
if (empty($args)) {
$types = array_keys($components);
@@ -252,10 +269,13 @@ function drush_features_components() {
elseif (drush_get_option(array('not-exported', 'o'), NULL)) {
$options['exported'] = FALSE;
}
if (drush_get_option(array('info-style', 'is'), NULL)) {
$options['info style'] = TRUE;
}
$filtered_components = _drush_features_component_filter($components, $args, $options);
if ($filtered_components){
_drush_features_component_print($filtered_components);
_drush_features_component_print($filtered_components, $options);
}
}
@@ -289,7 +309,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
// First filter on exported state.
foreach ($all_components as $source => $components) {
foreach ($components as $name => $title) {
$exported = sizeof($components_map[$source][$name]) > 0;
$exported = !empty($components_map[$source][$name]);
if ($exported) {
if ($options['exported']) {
$pool[$source][$name] = $title;
@@ -317,7 +337,8 @@ function _drush_features_component_filter($all_components, $patterns = array(),
// Rewrite * to %. Let users use both as wildcard.
$pattern = strtr($pattern, array('*' => '%'));
$sources = array();
list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
$source_pattern = strtok($pattern, ':');
$component_pattern = strtok(':');
// If source is empty, use a pattern.
if ($source_pattern == '') {
$source_pattern = '%';
@@ -382,7 +403,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
return drush_set_error('', dt('Ambiguous component "!component", matches !matches', array('!component' => $component_pattern, '!matches' => join(', ', $matches))));
}
}
if (!is_array($selected[$source])) {
if (empty($selected[$source])) {
$selected[$source] = array();
}
$selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
@@ -403,7 +424,7 @@ function _drush_features_component_filter($all_components, $patterns = array(),
if ($options['provided by'] && $options['exported'] ) {
foreach ($selected as $source => $components) {
foreach ($components as $name => $title) {
$exported = sizeof($components_map[$source][$name]) > 0;
$exported = !empty($components_map[$source][$name]);
if ($exported) {
$provided_by[$source . ':' . $name] = join(', ', $components_map[$source][$name]);
}
@@ -420,11 +441,17 @@ function _drush_features_component_filter($all_components, $patterns = array(),
/**
* Prints a list of filtered components.
*/
function _drush_features_component_print($filtered_components) {
function _drush_features_component_print($filtered_components, $options = array()) {
$rows = array(array(dt('Available sources')));
foreach ($filtered_components['components'] as $source => $components) {
foreach ($components as $name => $value) {
$row = array($source .':'. $name);
if (!empty($options['info style'])) {
// Output as .info file style.
$row = array('features[' . $source . '][] = "' . $name . '"');
}
else {
$row = array($source .':'. $name);
}
if (isset($filtered_components['sources'][$source .':'. $name])) {
$row[] = dt('Provided by') . ': ' . $filtered_components['sources'][$source .':'. $name];
}
@@ -446,9 +473,11 @@ function drush_features_export() {
return drush_set_error('', 'No components supplied.');
}
$components = _drush_features_component_list();
$options = array(
'exported' => FALSE,
);
$options = array();
if (!drush_get_option('ignore-conflicts', FALSE)) {
$options['exported'] = FALSE;
}
$filtered_components = _drush_features_component_filter($components, $args, $options);
$items = $filtered_components['components'];
@@ -568,11 +597,9 @@ function _drush_features_export($info, $module_name = NULL, $directory = NULL) {
$destination = drush_get_option(array('destination'), variable_get('features_default_export_path', FEATURES_DEFAULT_EXPORT_PATH));
$directory = isset($directory) ? $directory : $destination . '/' . $module_name;
if (is_dir($directory)) {
$warning = dt('Module appears to already exist in !dir', array('!dir' => $directory));
drush_log($warning, 'warning');
// If we aren't skipping confirmation and the directory already exists,
// prompt the user.
if (!$skip_confirmation && !drush_confirm(dt('Do you really want to continue?'))) {
// prompt the user. This message most make sense for but fe and fu.
if (!$skip_confirmation && !drush_confirm(dt('Module located at !dir will be updated. Do you want to continue?', array('!dir' => $directory)))) {
drush_die('Aborting.');
}
}
@@ -725,8 +752,13 @@ function drush_features_revert() {
$dt_args['@component'] = $component;
$confirmation_message = 'Do you really want to revert @module.@component?';
if ($skip_confirmation || drush_confirm(dt($confirmation_message, $dt_args))) {
features_revert(array($module => array($component)));
drush_log(dt('Reverted @module.@component.', $dt_args), 'ok');
if (features_feature_is_locked($module, $component)) {
drush_log(dt('Skipping locked @module.@component.', $dt_args), 'ok');
}
else {
features_revert(array($module => array($component)));
drush_log(dt('Reverted @module.@component.', $dt_args), 'ok');
}
}
else {
drush_log(dt('Skipping @module.@component.', $dt_args), 'ok');
@@ -882,6 +914,62 @@ function drush_features_diff() {
}
}
/**
* Diff all enabled features that are not in their default state.
*
* @param ...
* (Optional) A list of features to exclude from being reverted.
*/
function drush_features_diff_all() {
module_load_include('inc', 'features', 'features.export');
$features_to_exclude = func_get_args();
$features_to_revert = array();
foreach (features_get_features(NULL, TRUE) as $module) {
if ($module->status && !in_array($module->name, $features_to_exclude)) {
switch (features_get_storage($module->name)) {
case FEATURES_OVERRIDDEN:
case FEATURES_NEEDS_REVIEW:
case FEATURES_REBUILDABLE:
$features_to_diff[] = $module->name;
break;
}
}
}
if ($features_to_diff) {
// Check if the user wants to apply the force option.
$force = drush_get_option('force');
if($force) {
foreach ($features_to_diff as $module) {
drush_print(dt('Diff for !module:', array('!module' => $module)));
drush_invoke_process(drush_sitealias_get_record('@self'), 'features-diff', array($module));
}
}
else {
drush_print(dt('A diff will be performed for the following modules: !modules',
array('!modules' => implode(', ', $features_to_diff))
));
if (drush_confirm(dt('Do you want to continue?'))) {
foreach ($features_to_diff as $module) {
if (drush_confirm(dt('Diff !module?', array('!module' => $module)))) {
drush_invoke_process(drush_sitealias_get_record('@self'), 'features-diff', array($module));
}
}
}
else {
return drush_user_abort('Aborting.');
}
}
}
}
/**
* Helper function to call drush_set_error().
*

View File

@@ -45,11 +45,11 @@ function features_populate($info, $module_name) {
* @return fully populated $export array.
*/
function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
static $processed = array();
features_include();
if ($reset) {
$processed = array();
drupal_static_reset(__FUNCTION__);
}
$processed = &drupal_static(__FUNCTION__, array());
features_include();
foreach ($pipe as $component => $data) {
// Convert already defined items to dependencies.
// _features_resolve_dependencies($data, $export, $module_name, $component);
@@ -385,10 +385,7 @@ function features_export_render($export, $module_name, $reset = FALSE) {
* Detect differences between DB and code components of a feature.
*/
function features_detect_overrides($module) {
static $cache;
if (!isset($cache)) {
$cache = array();
}
$cache = &drupal_static(__FUNCTION__, array());
if (!isset($cache[$module->name])) {
// Rebuild feature from .info file description and prepare an export from current DB state.
$export = features_populate($module->info, $module->name);
@@ -713,10 +710,10 @@ function features_semaphore($op, $component) {
* Get normal objects for a given module/component pair.
*/
function features_get_normal($component, $module_name, $reset = FALSE) {
static $cache;
if (!isset($cache) || $reset) {
$cache = array();
if ($reset) {
drupal_static_reset(__FUNCTION__);
}
$cache = &drupal_static(__FUNCTION__, array());
if (!isset($cache[$module_name][$component])) {
features_include();
$code = NULL;
@@ -746,7 +743,7 @@ function features_get_normal($component, $module_name, $reset = FALSE) {
* Get defaults for a given module/component pair.
*/
function features_get_default($component, $module_name = NULL, $alter = TRUE, $reset = FALSE) {
static $cache = array();
$cache = &drupal_static(__FUNCTION__, array());
$alter = !empty($alter); // ensure $alter is a true/false boolean
features_include();
features_include_defaults($component);
@@ -820,7 +817,7 @@ function features_get_default($component, $module_name = NULL, $alter = TRUE, $r
* Get a map of components to their providing modules.
*/
function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
static $map = array();
$map = &drupal_static(__FUNCTION__, array());
global $features_ignore_conflicts;
if ($features_ignore_conflicts) {
@@ -866,10 +863,10 @@ 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) {
static $cache;
if (!isset($cache) || $reset) {
$cache = array();
if ($reset) {
drupal_static_reset(__FUNCTION__);
}
$cache = &drupal_static(__FUNCTION__, array());
$all_features = features_get_features();
$features = !empty($features) ? $features : array_keys($all_features);

View File

@@ -6,9 +6,9 @@ files[] = tests/features.test
configure = admin/structure/features/settings
; Information added by drupal.org packaging script on 2013-10-17
version = "7.x-2.0+0-dev"
; Information added by Drupal.org packaging script on 2015-04-13
version = "7.x-2.5"
core = "7.x"
project = "features"
datestamp = "1382036080"
datestamp = "1428944073"

View File

@@ -25,6 +25,16 @@ function features_uninstall() {
variable_del('features_default_export_path');
variable_del('features_semaphore');
variable_del('features_ignored_orphans');
variable_del('features_feature_locked');
variable_del('features_lock_mode');
db_delete('variable')
->condition('name', 'features_admin_show_component_%', 'LIKE')
->execute();
db_delete('variable')
->condition('name', 'features_component_locked_%', 'LIKE')
->execute();variable_del('features_component_locked_' . $component);
if (db_table_exists('menu_custom')) {
db_delete('menu_custom')
->condition('menu_name', 'features')
@@ -53,7 +63,7 @@ function _features_install_menu() {
/**
* Update 6100: Set module on all feature node types to 'features'.
*
* This update can be re-run as needed to repair any node types that are not
* removed after disabling the associated feature.
*

View File

@@ -100,33 +100,6 @@ jQuery.fn.sortElements = (function(){
}).trigger('change');
});
// Export form machine-readable JS
$('.feature-name:not(.processed)', context).each(function() {
$('.feature-name')
.addClass('processed')
.after(' <small class="feature-module-name-suffix">&nbsp;</small>');
if ($('.feature-module-name').val() === $('.feature-name').val().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/_+/g, '_') || $('.feature-module-name').val() === '') {
$('.feature-module-name').parents('.form-item').hide();
$('.feature-name').bind('keyup change', function() {
var machine = $(this).val().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/_+/g, '_');
if (machine !== '_' && machine !== '') {
$('.feature-module-name').val(machine);
$('.feature-module-name-suffix').empty().append(' Machine name: ' + machine + ' [').append($('<a href="#">'+ Drupal.t('Edit') +'</a>').click(function() {
$('.feature-module-name').parents('.form-item').show();
$('.feature-module-name-suffix').hide();
$('.feature-name').unbind('keyup');
return false;
})).append(']');
}
else {
$('.feature-module-name').val(machine);
$('.feature-module-name-suffix').text('');
}
});
$('.feature-name').keyup();
}
});
//View info dialog
var infoDialog = $('#features-info-file');
if (infoDialog.length != 0) {
@@ -184,7 +157,6 @@ jQuery.fn.sortElements = (function(){
}
function updateComponentCountInfo(item, section) {
console.log(section);
switch (section) {
case 'select':
var parent = $(item).closest('.features-export-list').siblings('.features-export-component');

View File

@@ -167,6 +167,17 @@ function features_menu() {
'file' => 'features.admin.inc',
);
}
$items['admin/structure/features/%feature/lock'] = array(
'title' => 'Lock',
'description' => 'Lock a feature or components.',
'page callback' => 'features_admin_lock',
'page arguments' => array(3, 5, 6),
'load arguments' => array(3, TRUE, TRUE),
'access arguments' => array('administer features'),
'type' => MENU_CALLBACK,
'file' => 'features.admin.inc',
);
$items['admin/structure/features/%feature/status'] = array(
'title' => 'Status',
'description' => 'Javascript status call back.',
@@ -214,11 +225,11 @@ function features_theme() {
$items = array();
$items['features_module_status'] = array(
'variables' => array('module' => null, 'status' => null)
'variables' => array('module' => NULL, 'status' => NULL)
) + $base;
$items['features_components'] = array(
'variables' => array('info' => null, 'sources' => null),
'variables' => array('info' => NULL, 'sources' => NULL),
) + $base;
$items['features_component_key'] = $base;
@@ -227,7 +238,11 @@ function features_theme() {
) + $base;
$items['features_storage_link'] = array(
'variables' => array('storage' => null, 'text' => null, 'path' => null, 'options' => array()),
'variables' => array('storage' => NULL, 'text' => NULL, 'path' => NULL, 'options' => array()),
) + $base;
$items['features_lock_link'] = array(
'variables' => array('feature' => NULL, 'component' => NULL, 'locked' => FALSE),
) + $base;
$items['features_form_components'] =
@@ -290,6 +305,11 @@ function features_permission() {
'description' => t('Allow feature exports to be generated and written directly to site.'),
'restrict access' => TRUE,
),
'rename features' => array(
'title' => t('Edit feature machine name'),
'description' => t('Allows editing machine name of a disabled feature'),
'restrict access' => TRUE,
),
);
}
@@ -299,10 +319,10 @@ function features_permission() {
function features_help($path, $arg) {
switch ($path) {
case 'admin/help#features':
$output = file_get_contents(drupal_get_path('module', 'features') .'/README.txt');
return module_exists('markdown') ? filter_xss_admin(module_invoke('markdown', 'filter', 'process', 0, -1, $output)) : '<pre>'. check_plain($output) .'</pre>';
$output = file_get_contents(drupal_get_path('module', 'features') . '/README.txt');
return module_exists('markdown') ? filter_xss_admin(module_invoke('markdown', 'filter', 'process', 0, -1, $output)) : '<pre>' . check_plain($output) . '</pre>';
case 'admin/build/features':
return '<p>'. t('A "Feature" is a certain type of Drupal module which contains a package of configuration that, when enabled, provides a new set of functionality for your Drupal site. Enable features by selecting the checkboxes below and clicking the Save configuration button. If the configuration of the feature has been changed its "State" will be either "overridden" or "needs review", otherwise it will be "default", indicating that the configuration has not been changed. Click on the state to see more details about the feature and its components.') .'</p>';
return '<p>' . t('A "Feature" is a certain type of Drupal module which contains a package of configuration that, when enabled, provides a new set of functionality for your Drupal site. Enable features by selecting the checkboxes below and clicking the Save configuration button. If the configuration of the feature has been changed its "State" will be either "overridden" or "needs review", otherwise it will be "default", indicating that the configuration has not been changed. Click on the state to see more details about the feature and its components.') . '</p>';
}
}
@@ -641,6 +661,10 @@ function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
$files = system_rebuild_module_data();
foreach ($files as $row) {
// Remove modification timestamp, added in Drupal 7.33.
if (isset($row->info['mtime'])) {
unset($row->info['mtime']);
}
// Avoid false-reported feature overrides for php = 5.2.4 line in .info file.
if (isset($row->info['php'])) {
unset($row->info['php']);
@@ -654,7 +678,7 @@ function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
if (!empty($row->info['features'])) {
// Fix css/js paths
if (!empty($row->info['stylesheets'])) {
foreach($row->info['stylesheets'] as $media => $css) {
foreach ($row->info['stylesheets'] as $media => $css) {
$row->info['stylesheets'][$media] = array_keys($css);
}
}
@@ -789,7 +813,7 @@ function features_get_conflicts($reset = FALSE) {
if (isset($component_info[$type]['duplicates']) && $component_info[$type]['duplicates'] == FEATURES_DUPLICATES_ALLOWED) {
continue;
}
else if (count($modules) > 1) {
elseif (count($modules) > 1) {
foreach ($modules as $module) {
if (!isset($conflicts[$module])) {
$conflicts[$module] = array();
@@ -840,7 +864,7 @@ function features_get_module_status($module) {
if (module_exists($module)) {
return FEATURES_MODULE_ENABLED;
}
else if (features_get_modules($module)) {
elseif (features_get_modules($module)) {
return FEATURES_MODULE_DISABLED;
}
else {
@@ -886,6 +910,7 @@ function features_form_system_modules_alter(&$form) {
* Restore the specified modules to the default state.
*/
function _features_restore($op, $items = array()) {
$lockable = FALSE;
// Set this variable in $conf if having timeout issues during install/rebuild.
if (variable_get('features_restore_time_limit_' . $op, FALSE) !== FALSE) {
drupal_set_time_limit(variable_get('features_restore_time_limit_' . $op, FALSE));
@@ -899,11 +924,13 @@ function _features_restore($op, $items = array()) {
$restore_states = array(FEATURES_OVERRIDDEN, FEATURES_REBUILDABLE, FEATURES_NEEDS_REVIEW);
$restore_hook = 'features_revert';
$log_action = 'Revert';
$lockable = TRUE;
break;
case 'rebuild':
$restore_states = array(FEATURES_REBUILDABLE);
$restore_hook = 'features_rebuild';
$log_action = 'Rebuild';
$lockable = variable_get('features_lock_mode', 'all') == 'all';
break;
case 'disable':
$restore_hook = 'features_disable_feature';
@@ -932,8 +959,20 @@ function _features_restore($op, $items = array()) {
}
}
// Invoke global pre restore hook.
module_invoke_all('features_pre_restore', $op, $items);
foreach ($items as $module_name => $components) {
// If feature is totally locked, do not execute past this stage.
if ($lockable && features_feature_is_locked($module_name)) {
watchdog('features', 'Tried @actioning a locked @module_name, aborted.', array('@action' => $log_action, '@module_name' => $module_name));
continue;
}
foreach ($components as $component) {
// If feature is totally locked, do not execute past this stage.
if ($lockable && features_feature_is_locked($module_name, $component)) {
watchdog('features', 'Tried @actioning a locked @module_name / @component, aborted.', array('@action' => $log_action, '@component' => $component, '@module_name' => $module_name));
continue;
}
// Invoke pre hook
$pre_hook = 'pre_' . $restore_hook;
module_invoke($module_name, $pre_hook, $component);
@@ -955,6 +994,8 @@ function _features_restore($op, $items = array()) {
module_invoke($module_name, $post_hook, $component);
}
}
// Invoke global post restore hook.
module_invoke_all('features_post_restore', $op, $items);
}
/**
@@ -1034,6 +1075,9 @@ function features_hook_info() {
* Change vocabularies permission, from vocab id to machine name and vice versa.
*/
function _user_features_change_term_permission(&$perm, $type = 'vid') {
if (!module_exists('taxonomy')) {
return;
}
// Export vocabulary permissions using the machine name, instead of vocabulary
// id.
if (strpos($perm, 'edit terms in ') !== FALSE || strpos($perm, 'delete terms in ') !== FALSE) {
@@ -1109,3 +1153,53 @@ function features_get_deprecated($components = array()) {
}
return $deprecated;
}
/**
* Returns whether a feature or it's component is locked.
*/
function features_feature_is_locked($feature, $component = NULL, $check_global_component_setting = TRUE) {
$locked = variable_get('features_feature_locked', array());
if ($component) {
return ($check_global_component_setting && features_component_is_locked($component)) || !empty($locked[$feature][$component]);
}
else {
return !empty($locked[$feature]['_all']);
}
}
/**
* Returns whether a component is locked.
*/
function features_component_is_locked($component) {
return variable_get('features_component_locked_' . $component, FALSE);
}
/**
* Locks a feature or it's component.
*/
function features_feature_lock($feature, $component = NULL) {
$locked = variable_get('features_feature_locked', array());
$locked[$feature] = !empty($locked[$feature]) ? $locked[$feature] : array();
if ($component) {
$locked[$feature][$component] = TRUE;
}
else {
$locked[$feature]['_all'] = TRUE;
}
variable_set('features_feature_locked', $locked);
}
/**
* Unlocks a feature or it's component.
*/
function features_feature_unlock($feature, $component = NULL) {
$locked = variable_get('features_feature_locked', array());
if ($component) {
unset($locked[$feature][$component]);
}
else {
unset($locked[$feature]['_all']);
}
variable_set('features_feature_locked', $locked);
}

View File

@@ -151,7 +151,7 @@ function field_base_features_export_render($module, $data, $export = NULL) {
foreach ($data as $identifier) {
if ($field = features_field_base_load($identifier)) {
unset($field['columns']);
// unset($field['locked']);
unset($field['foreign keys']);
// Only remove the 'storage' declaration if the field is using the default
// storage type.
if ($field['storage']['type'] == variable_get('field_storage_default', 'field_sql_storage')) {
@@ -166,8 +166,15 @@ function field_base_features_export_render($module, $data, $export = NULL) {
_field_instance_features_export_sort($field);
$field_export = features_var_export($field, ' ');
$field_prefix = ' // Exported field_base: ';
$field_identifier = features_var_export($identifier);
$code[] = " // Exported field_base: {$field_identifier}";
if (features_field_export_needs_wrap($field_prefix, $field_identifier)) {
$code[] = rtrim($field_prefix);
$code[] = " // {$field_identifier}";
}
else {
$code[] = $field_prefix . $field_identifier;
}
$code[] = " \$field_bases[{$field_identifier}] = {$field_export};";
$code[] = "";
}
@@ -190,8 +197,15 @@ function field_instance_features_export_render($module, $data, $export = NULL) {
if ($instance = features_field_instance_load($identifier)) {
_field_instance_features_export_sort($instance);
$field_export = features_var_export($instance, ' ');
$instance_prefix = ' // Exported field_instance: ';
$instance_identifier = features_var_export($identifier);
$code[] = " // Exported field_instance: {$instance_identifier}";
if (features_field_export_needs_wrap($instance_prefix, $instance_identifier)) {
$code[] = rtrim($instance_prefix);
$code[] = " // {$instance_identifier}";
}
else {
$code[] = $instance_prefix . $instance_identifier;
}
$code[] = " \$field_instances[{$instance_identifier}] = {$field_export};";
$code[] = "";
@@ -528,3 +542,23 @@ function features_field_load($identifier) {
}
return FALSE;
}
/**
* Determine if a field export line needs to be wrapped.
*
* Drupal code standards specify that comments should wrap at 80 characters or
* less.
*
* @param string $prefix
* The prefix to be exported before the field identifier.
* @param string $identifier
* The field identifier.
*
* @return BOOL
* TRUE if the line should be wrapped after the prefix, else FALSE.
*
* @see https://www.drupal.org/node/1354
*/
function features_field_export_needs_wrap($prefix, $identifier) {
return (strlen($prefix) + strlen($identifier) > 80);
}

View File

@@ -86,16 +86,25 @@ function image_features_revert($module) {
/**
* Remove unnecessary keys for export.
*/
function _image_features_style_sanitize(&$style, $child = FALSE) {
$omit = $child ? array('isid', 'ieid', 'storage') : array('isid', 'ieid', 'storage', 'module');
if (is_array($style)) {
foreach ($style as $k => $v) {
if (in_array($k, $omit, TRUE)) {
unset($style[$k]);
}
else if (is_array($v)) {
_image_features_style_sanitize($style[$k], TRUE);
}
}
function _image_features_style_sanitize(array &$style) {
// Sanitize style: Don't export numeric IDs and things which get overwritten
// in image_styles() or are code/storage specific. The name property will be
// the key of the exported $style array.
$style = array_diff_key($style, array_flip(array(
'isid',
'name',
'module',
'storage',
)));
// Sanitize effects: all that needs to be kept is name, weight and data,
// which holds all the style-specific configuration. Other keys are assumed
// to belong to the definition of the effect itself, so not configuration.
foreach ($style['effects'] as $id => $effect) {
$style['effects'][$id] = array_intersect_key($effect, array_flip(array(
'name',
'data',
'weight',
)));
}
}

View File

@@ -134,6 +134,7 @@ function _features_language_save($language) {
->fields(array(
'plurals' => empty($language->plurals) ? 0 : $language->plurals,
'formula' => empty($language->formula) ? '' : $language->formula,
'weight' => empty($language->weight) ? 0 : $language->weight,
))
->condition('language', $language->language)
->execute();

View File

@@ -241,7 +241,8 @@ function menu_links_features_export_render($module, $data, $export = NULL) {
// Don't show new identifier unless we are actually exporting.
$link['options']['identifier'] = $new_identifier;
// identifiers are renewed, => that means we need to update them in the db
menu_link_save($temp = $link);
$temp = $link;
menu_link_save($temp);
}
unset($link['plid']);
@@ -295,19 +296,27 @@ function menu_links_features_rebuild_ordered($menu_links, $reset = FALSE) {
foreach ($unordered as $key => $link) {
$identifier = menu_links_features_identifier($link);
$parent = isset($link['parent_identifier']) ? $link['parent_identifier'] : '';
if (empty($parent)) {
$ordered[$identifier] = 0;
$all_links[$identifier] = $link;
unset($unordered[$key]);
$weight = 0;
// Parent has been seen, so weigh this above parent.
if (isset($ordered[$parent])) {
$weight = $ordered[$parent] + 1;
}
elseif (isset($ordered[$parent])) {
$ordered[$identifier] = $ordered[$parent] + 1;
$all_links[$identifier] = $link;
unset($unordered[$key]);
// Next loop will try to find parent weight instead.
elseif ($parent) {
continue;
}
$ordered[$identifier] = $weight;
$all_links[$identifier] = $link;
unset($unordered[$key]);
}
// Exit out when the above does no changes this loop.
} while (count($unordered) < $current);
}
// Add all remaining unordered items to the ordered list.
foreach ($unordered as $link) {
$identifier = menu_links_features_identifier($link);
$ordered[$identifier] = 0;
}
asort($ordered);
}
@@ -356,10 +365,11 @@ function features_menu_link_load($identifier) {
list($menu_name, $link_path) = explode(':', $identifier, 2);
}
$links = db_select('menu_links')
->fields('menu_links', array('menu_name', 'mlid', 'plid', 'link_path', 'router_path', 'link_title', 'options', 'module', 'hidden', 'external', 'has_children', 'expanded', 'weight', 'customized'))
->condition('menu_name', $menu_name)
->condition('link_path', $link_path)
->execute()
->fields('menu_links', array('menu_name', 'mlid', 'plid', 'link_path', 'router_path', 'link_title', 'options', 'module', 'hidden', 'external', 'has_children', 'expanded', 'weight', 'customized'))
->condition('menu_name', $menu_name)
->condition('link_path', $link_path)
->addTag('features_menu_link')
->execute()
->fetchAllAssoc('mlid');
foreach($links as $link) {
@@ -391,11 +401,14 @@ function features_menu_link_load($identifier) {
foreach($links as $link) {
$link->options = unserialize($link->options);
// title or previous identifier matches
if ((isset($link->options['identifier']) && strcmp($link->options['identifier'], $identifier) == 0)
|| (isset($clean_title) && strcmp(features_clean_title($link->link_title), $clean_title) == 0)) {
// Links with a stored identifier must only be matched on that identifier,
// to prevent cross over assumptions.
if (isset($link->options['identifier'])) {
if (strcmp($link->options['identifier'], $identifier) == 0) {
return (array)$link;
}
}
elseif ((strcmp(features_clean_title($link->link_title), $clean_title) == 0)) {
return (array)$link;
}
}

View File

@@ -9,6 +9,7 @@ function node_features_api() {
'name' => t('Content types'),
'feature_source' => TRUE,
'default_hook' => 'node_info',
'alter_type' => FEATURES_ALTER_TYPE_INLINE,
),
);
}
@@ -86,6 +87,7 @@ function node_features_export_render($module, $data, $export = NULL) {
}
}
$output[] = ' );';
$output[] = ' drupal_alter(\'node_info\', $items);';
$output[] = ' return $items;';
$output = implode("\n", $output);
return array('node_info' => $output);
@@ -115,7 +117,7 @@ function node_features_revert($module = NULL) {
}
/**
* Implements hook_features_disable().
* Implements hook_features_disable_feature().
*
* When a features module is disabled, modify any node types it provides so
* they can be deleted manually through the content types UI.
@@ -123,7 +125,7 @@ function node_features_revert($module = NULL) {
* @param $module
* Name of module that has been disabled.
*/
function node_features_disable($module) {
function node_features_disable_feature($module) {
if ($default_types = features_get_default('node', $module)) {
foreach ($default_types as $type_name => $type_info) {
$type_info = node_type_load($type_name);
@@ -131,13 +133,14 @@ function node_features_disable($module) {
$type_info->custom = 1;
$type_info->modified = 1;
$type_info->locked = 0;
$type_info->disabled = 0;
node_type_save($type_info);
}
}
}
/**
* Implements hook_features_enable().
* Implements hook_features_enable_feature().
*
* 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.
@@ -145,7 +148,7 @@ function node_features_disable($module) {
* @param $module
* Name of module that has been enabled.
*/
function node_features_enable($module) {
function node_features_enable_feature($module) {
if ($default_types = features_get_default('node', $module)) {
foreach ($default_types as $type_name => $type_info) {
// Ensure the type exists.
@@ -154,6 +157,7 @@ function node_features_enable($module) {
$type_info->custom = 0;
$type_info->modified = 0;
$type_info->locked = 1;
$type_info->disabled = 0;
node_type_save($type_info);
}
}

View File

@@ -50,12 +50,12 @@ function user_permission_features_export_options() {
$modules = array();
$module_info = system_get_info('module');
foreach (module_implements('permission') as $module) {
$modules[$module_info[$module]['name']] = $module;
$modules[$module] = $module_info[$module]['name'];
}
ksort($modules);
$options = array();
foreach ($modules as $display_name => $module) {
foreach ($modules as $module => $display_name) {
if ($permissions = module_invoke($module, 'permission')) {
foreach ($permissions as $perm => $perm_item) {
// Export vocabulary permissions using the machine name, instead of

View File

@@ -29,16 +29,8 @@ function features_test_image_default_styles() {
// Exported image style: features_test.
$styles['features_test'] = array(
'name' => 'features_test',
'effects' => array(
2 => array(
'label' => 'Scale',
'help' => 'Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.',
'effect callback' => 'image_scale_effect',
'dimensions callback' => 'image_scale_dimensions',
'form callback' => 'image_scale_form',
'summary theme' => 'image_scale_summary',
'module' => 'image',
'name' => 'image_scale',
'data' => array(
'width' => 100,

View File

@@ -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 2013-10-17
version = "7.x-2.0+0-dev"
; Information added by Drupal.org packaging script on 2015-04-13
version = "7.x-2.5"
core = "7.x"
project = "features"
datestamp = "1382036080"
datestamp = "1428944073"

View File

@@ -3,7 +3,7 @@
<div class='clearfix features-components'>
<div class='column'>
<div class='info'>
<h3><?php print $name ?></h3>
<?php print $lock_feature ?><h3><?php print $name ?></h3>
<div class='description'><?php print $description ?></div>
<?php print $dependencies ?>
</div>

View File

@@ -81,6 +81,7 @@ function template_preprocess_features_admin_components(&$vars) {
// Other elements
$vars['buttons'] = drupal_render($form['buttons']);
$vars['form'] = $form;
$vars['lock_feature'] = theme('features_lock_link', array('feature' => $form['#feature']->name));
}
/**
@@ -109,6 +110,36 @@ function theme_features_module_status($vars) {
return "<span class=\"$class\">$text</span>";
}
/**
* Themes a lock link
*/
function theme_features_lock_link($vars) {
drupal_add_library('system', 'ui');
drupal_add_library('system', 'drupal.ajax');
$component = $vars['component'] ? $vars['component'] : '';
if ($component && features_component_is_locked($component)) {
return l(t('Component locked'), 'admin/structure/features/settings', array(
'attributes' => array(
'class' => 'features-lock-icon ui-icon ui-icon-locked',
'title' => t('This component is locked on a global level.'),
),
'fragment' => 'edit-lock-components',
));
}
$feature = $vars['feature'];
$is_locked = features_feature_is_locked($feature, $component);
$options = array(
'attributes' => array(
'class' => array('use-ajax features-lock-icon ui-icon ' . ($is_locked ? ' ui-icon-locked' : ' ui-icon-unlocked')),
'id' => 'features-lock-link-' . $feature . ($component ? '-' . $component : ''),
'title' => $is_locked ? t('This item is locked and features will not be rebuilt or reverted.') : t('This item is unlocked and will be rebuilt/reverted as normal.'),
),
'query' => array('token' => drupal_get_token('features/' . $feature . '/' . $component)),
);
$path = "admin/structure/features/" . $feature . "/lock/nojs" . ($component ? '/' . $component: '');
return l($is_locked ? t('UnLock') : t('Lock'), $path, $options);
}
/**
* Themes a module status display.
*/