uppdated modules
This commit is contained in:
@@ -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(
|
||||
@@ -203,7 +209,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 +245,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 +603,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();
|
||||
@@ -755,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();
|
||||
@@ -843,7 +850,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 +907,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 +1009,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.',
|
||||
@@ -1073,6 +1095,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',
|
||||
@@ -1102,7 +1125,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])) {
|
||||
@@ -1126,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',
|
||||
@@ -1311,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)) {
|
||||
@@ -1328,21 +1351,26 @@ function features_form_submit(&$form, &$form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Form for clearing cache after enabling a feature.
|
||||
* Submit handler for the 'manage features' form rebuild button.
|
||||
*/
|
||||
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) {
|
||||
drupal_flush_all_caches();
|
||||
function features_form_rebuild() {
|
||||
cache_clear_all('features:features_list', 'cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for clearing cache after enabling a feature.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1587,3 +1615,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;
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
*
|
||||
@@ -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();
|
||||
@@ -309,11 +310,31 @@ 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';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @{
|
||||
|
||||
@@ -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.",
|
||||
@@ -43,6 +49,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 +79,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 +144,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;
|
||||
}
|
||||
|
||||
@@ -191,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:
|
||||
@@ -214,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,10 +278,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +318,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;
|
||||
@@ -318,7 +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();
|
||||
list($source_pattern, $component_pattern) = explode(':', $pattern, 2);
|
||||
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 = '%';
|
||||
@@ -383,7 +417,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));
|
||||
@@ -404,7 +438,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]);
|
||||
}
|
||||
@@ -421,11 +455,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];
|
||||
}
|
||||
@@ -447,9 +487,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'];
|
||||
@@ -579,9 +621,40 @@ 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) {
|
||||
// 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';
|
||||
@@ -635,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);
|
||||
@@ -747,7 +820,7 @@ function drush_features_revert() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_features_list();
|
||||
drush_print_table(drush_features_list());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -801,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];
|
||||
@@ -886,6 +959,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().
|
||||
*
|
||||
|
||||
@@ -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.
|
||||
$language = _features_export_language();
|
||||
|
||||
if ($reset) {
|
||||
drupal_static_reset(__FUNCTION__);
|
||||
}
|
||||
@@ -89,6 +92,7 @@ function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE)
|
||||
}
|
||||
}
|
||||
}
|
||||
_features_export_language($language);
|
||||
return $export;
|
||||
}
|
||||
|
||||
@@ -295,6 +299,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 +314,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 +413,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 +427,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 +682,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 +739,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 +757,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.
|
||||
*/
|
||||
@@ -815,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());
|
||||
@@ -863,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__);
|
||||
}
|
||||
@@ -873,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')) {
|
||||
@@ -956,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;
|
||||
}
|
||||
|
||||
@@ -970,25 +1012,49 @@ 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) {
|
||||
$array = features_remove_recursion($array);
|
||||
if (isset($component)) {
|
||||
$ignore_keys = _features_get_ignore_keys($component);
|
||||
// remove keys to be ignored
|
||||
if (count($ignore_keys)) {
|
||||
_features_remove_ignores($array, $ignore_keys);
|
||||
}
|
||||
}
|
||||
_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 +1076,92 @@ 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.
|
||||
*
|
||||
* Taken from https://code.google.com/p/formaldehyde/source/browse/trunk/formaldehyde.php
|
||||
* Also used in node_export module
|
||||
*
|
||||
* @param $o mixed
|
||||
* @return mixed
|
||||
* returns a copy of the object or array with recursion removed
|
||||
*/
|
||||
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;
|
||||
}
|
||||
$serialize = substr($serialize, 0, $last) . preg_replace_callback($re, $replace, substr($serialize, $last));
|
||||
$o = unserialize($serialize);
|
||||
}
|
||||
}
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
|
||||
@@ -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-01-05
|
||||
version = "7.x-2.3"
|
||||
; Information added by Drupal.org packaging script on 2016-04-18
|
||||
version = "7.x-2.10"
|
||||
core = "7.x"
|
||||
project = "features"
|
||||
datestamp = "1420492083"
|
||||
datestamp = "1461011641"
|
||||
|
||||
|
||||
@@ -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().
|
||||
*/
|
||||
@@ -27,10 +36,14 @@ function features_uninstall() {
|
||||
variable_del('features_ignored_orphans');
|
||||
variable_del('features_feature_locked');
|
||||
variable_del('features_lock_mode');
|
||||
foreach (array_keys(features_get_components()) as $component) {
|
||||
variable_del('features_admin_show_component_' . $component);
|
||||
variable_del('features_component_locked_' . $component);
|
||||
}
|
||||
|
||||
db_delete('variable')
|
||||
->condition('name', 'features_admin_show_component_%', 'LIKE')
|
||||
->execute();
|
||||
db_delete('variable')
|
||||
->condition('name', 'features_component_locked_%', 'LIKE')
|
||||
->execute();
|
||||
|
||||
if (db_table_exists('menu_custom')) {
|
||||
db_delete('menu_custom')
|
||||
->condition('menu_name', 'features')
|
||||
@@ -126,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);
|
||||
|
||||
@@ -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,
|
||||
@@ -268,13 +267,20 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (db_table_exists('cache_features')) {
|
||||
return array('cache_features');
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -349,6 +355,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 +400,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");
|
||||
@@ -503,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.
|
||||
@@ -535,13 +550,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 +622,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 +667,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,14 +755,14 @@ 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -892,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];
|
||||
}
|
||||
@@ -903,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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1067,6 +1086,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'));
|
||||
}
|
||||
@@ -1075,6 +1095,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) {
|
||||
@@ -1186,7 +1209,6 @@ function features_feature_lock($feature, $component = NULL) {
|
||||
variable_set('features_feature_locked', $locked);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unlocks a feature or it's component.
|
||||
*/
|
||||
@@ -1200,3 +1222,73 @@ function features_feature_unlock($feature, $component = NULL) {
|
||||
}
|
||||
variable_set('features_feature_locked', $locked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/Returns the current language to english to ensure a proper export.
|
||||
*/
|
||||
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',
|
||||
'name' => 'English',
|
||||
'native' => 'English',
|
||||
'direction' => 0,
|
||||
'enabled' => 1,
|
||||
'plurals' => 0,
|
||||
'formula' => '',
|
||||
'domain' => '',
|
||||
'prefix' => '',
|
||||
'weight' => 0,
|
||||
'javascript' => '',
|
||||
);
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,14 +151,14 @@ 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')) {
|
||||
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']);
|
||||
@@ -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[] = "";
|
||||
|
||||
@@ -260,12 +274,23 @@ 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) {
|
||||
field_update_field($field);
|
||||
$array_diff_result = drupal_array_diff_assoc_recursive($field + $existing_field, $existing_field);
|
||||
if (!empty($array_diff_result)) {
|
||||
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);
|
||||
@@ -395,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']);
|
||||
@@ -469,7 +494,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);
|
||||
}
|
||||
@@ -528,3 +554,24 @@ 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) {
|
||||
// Check for 79 characters, since the comment ends with a full stop.
|
||||
return (strlen($prefix) + strlen($identifier) > 79);
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,14 +134,15 @@ 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();
|
||||
}
|
||||
// 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,16 +247,16 @@ 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'];
|
||||
}
|
||||
}
|
||||
$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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -117,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.
|
||||
@@ -125,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);
|
||||
@@ -133,22 +133,26 @@ 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.
|
||||
*
|
||||
* Update the database cache of node types if needed.
|
||||
*
|
||||
* @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)) {
|
||||
$rebuild = FALSE;
|
||||
foreach ($default_types as $type_name => $type_info) {
|
||||
// Ensure the type exists.
|
||||
if ($type_info = node_type_load($type_name)) {
|
||||
@@ -156,8 +160,15 @@ 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);
|
||||
}
|
||||
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';
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
-8
@@ -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,
|
||||
|
||||
@@ -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-01-05
|
||||
version = "7.x-2.3"
|
||||
; Information added by Drupal.org packaging script on 2016-04-18
|
||||
version = "7.x-2.10"
|
||||
core = "7.x"
|
||||
project = "features"
|
||||
datestamp = "1420492083"
|
||||
datestamp = "1461011641"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user