annotate sites/all/modules/features/features.export.inc @ 9:830c812b520f

added smtp module
author root <root@paio.local>
date Mon, 28 Oct 2013 15:34:27 +0000
parents ce11bbd8f642
children
rev   line source
danielebarchiesi@4 1 <?php
danielebarchiesi@4 2
danielebarchiesi@4 3 /**
danielebarchiesi@4 4 * @param $info - feature info array
danielebarchiesi@4 5 * @param $module_name
danielebarchiesi@4 6 * @return fully populated export array
danielebarchiesi@4 7 */
danielebarchiesi@4 8 function features_populate($info, $module_name) {
danielebarchiesi@4 9 // Sanitize items.
danielebarchiesi@4 10 $items = !empty($info['features']) ? array_filter($info['features']) : array();
danielebarchiesi@4 11 $items['dependencies'] = !empty($info['dependencies']) ? drupal_map_assoc(array_filter($info['dependencies'])) : array();
danielebarchiesi@4 12
danielebarchiesi@4 13 // Populate stub
danielebarchiesi@4 14 $stub = array('features' => array(), 'dependencies' => array(), 'conflicts' => array()) + $info + array('features_exclude' => array());
danielebarchiesi@4 15 $export = _features_populate($items, $stub, $module_name, TRUE);
danielebarchiesi@4 16
danielebarchiesi@4 17 // Add Features API version. Any module with this entry in the .info file
danielebarchiesi@4 18 // will be treated as a Feature and included in the admin/build/features UI.
danielebarchiesi@4 19 $export['features']['features_api']['api:' . FEATURES_API] = TRUE;
danielebarchiesi@4 20 // Allow other modules to alter the export.
danielebarchiesi@4 21 drupal_alter('features_export', $export, $module_name);
danielebarchiesi@4 22
danielebarchiesi@4 23 // Clean up and standardize order
danielebarchiesi@4 24 foreach (array_keys($export['features']) as $k) {
danielebarchiesi@4 25 ksort($export['features'][$k]);
danielebarchiesi@4 26 }
danielebarchiesi@4 27 ksort($export['features']);
danielebarchiesi@4 28 ksort($export['dependencies']);
danielebarchiesi@4 29 ksort($export['features_exclude']);
danielebarchiesi@4 30
danielebarchiesi@4 31 return $export;
danielebarchiesi@4 32 }
danielebarchiesi@4 33
danielebarchiesi@4 34 /**
danielebarchiesi@4 35 * Iterate and descend into a feature definition to extract module
danielebarchiesi@4 36 * dependencies and feature definition. Calls hook_features_export for modules
danielebarchiesi@4 37 * that implement it.
danielebarchiesi@4 38 *
danielebarchiesi@4 39 * @param $pipe
danielebarchiesi@4 40 * Associative of array of module => info-for-module
danielebarchiesi@4 41 * @param $export
danielebarchiesi@4 42 * Associative array of items, and module dependencies which define a feature.
danielebarchiesi@4 43 * Passed by reference.
danielebarchiesi@4 44 *
danielebarchiesi@4 45 * @return fully populated $export array.
danielebarchiesi@4 46 */
danielebarchiesi@4 47 function _features_populate($pipe, &$export, $module_name = '', $reset = FALSE) {
danielebarchiesi@4 48 static $processed = array();
danielebarchiesi@4 49 features_include();
danielebarchiesi@4 50 if ($reset) {
danielebarchiesi@4 51 $processed = array();
danielebarchiesi@4 52 }
danielebarchiesi@4 53 foreach ($pipe as $component => $data) {
danielebarchiesi@4 54 // Convert already defined items to dependencies.
danielebarchiesi@4 55 // _features_resolve_dependencies($data, $export, $module_name, $component);
danielebarchiesi@4 56 // Remove any excluded items.
danielebarchiesi@4 57 if (!empty($export['features_exclude'][$component])) {
danielebarchiesi@4 58 $data = array_diff($data, $export['features_exclude'][$component]);
danielebarchiesi@4 59 if ($component == 'dependencies' && !empty($export['dependencies'])) {
danielebarchiesi@4 60 $export['dependencies'] = array_diff($export['dependencies'], $export['features_exclude'][$component]);
danielebarchiesi@4 61 }
danielebarchiesi@4 62 }
danielebarchiesi@4 63 if (!empty($data) && $function = features_hook($component, 'features_export')) {
danielebarchiesi@4 64 // Pass module-specific data and export array.
danielebarchiesi@4 65 // We don't use features_invoke() here since we need to pass $export by reference.
danielebarchiesi@4 66 $more = $function($data, $export, $module_name, $component);
danielebarchiesi@4 67 // Add the context information.
danielebarchiesi@4 68 $export['component'] = $component;
danielebarchiesi@4 69 $export['module_name'] = $module_name;
danielebarchiesi@4 70 // Allow other modules to manipulate the pipe to add in additional modules.
danielebarchiesi@4 71 drupal_alter(array('features_pipe', 'features_pipe_' . $component), $more, $data, $export);
danielebarchiesi@4 72 // Remove the component information.
danielebarchiesi@4 73 unset($export['component']);
danielebarchiesi@4 74 unset($export['module_name']);
danielebarchiesi@4 75 // Allow for export functions to request additional exports, but avoid
danielebarchiesi@4 76 // circular references on already processed components.
danielebarchiesi@4 77 $processed[$component] = isset($processed[$component]) ? array_merge($processed[$component], $data) : $data;
danielebarchiesi@4 78
danielebarchiesi@4 79 if (!empty($more)) {
danielebarchiesi@4 80 // Remove already processed components.
danielebarchiesi@4 81 foreach ($more as $component_name => $component_data) {
danielebarchiesi@4 82 if (isset($processed[$component_name])) {
danielebarchiesi@4 83 $more[$component_name] = array_diff($component_data, $processed[$component_name]);
danielebarchiesi@4 84 }
danielebarchiesi@4 85 }
danielebarchiesi@4 86 if ($more = array_filter($more)) {
danielebarchiesi@4 87 _features_populate($more, $export, $module_name);
danielebarchiesi@4 88 }
danielebarchiesi@4 89 }
danielebarchiesi@4 90 }
danielebarchiesi@4 91 }
danielebarchiesi@4 92 return $export;
danielebarchiesi@4 93 }
danielebarchiesi@4 94
danielebarchiesi@4 95 /**
danielebarchiesi@4 96 * Iterates over data and convert to dependencies if already defined elsewhere.
danielebarchiesi@4 97 */
danielebarchiesi@4 98 function _features_resolve_dependencies(&$data, &$export, $module_name, $component) {
danielebarchiesi@4 99 if ($map = features_get_default_map($component)) {
danielebarchiesi@4 100 foreach ($data as $key => $item) {
danielebarchiesi@4 101 // If this node type is provided by a different module, add it as a dependency
danielebarchiesi@4 102 if (isset($map[$item]) && $map[$item] != $module_name) {
danielebarchiesi@4 103 $export['dependencies'][$map[$item]] = $map[$item];
danielebarchiesi@4 104 unset($data[$key]);
danielebarchiesi@4 105 }
danielebarchiesi@4 106 }
danielebarchiesi@4 107 }
danielebarchiesi@4 108 }
danielebarchiesi@4 109
danielebarchiesi@4 110 /**
danielebarchiesi@4 111 * Iterates over a list of dependencies and kills modules that are
danielebarchiesi@4 112 * captured by other modules 'higher up'.
danielebarchiesi@4 113 */
danielebarchiesi@4 114 function _features_export_minimize_dependencies($dependencies, $module_name = '') {
danielebarchiesi@4 115 // Ensure that the module doesn't depend upon itself
danielebarchiesi@4 116 if (!empty($module_name) && !empty($dependencies[$module_name])) {
danielebarchiesi@4 117 unset($dependencies[$module_name]);
danielebarchiesi@4 118 }
danielebarchiesi@4 119
danielebarchiesi@4 120 // Do some cleanup:
danielebarchiesi@4 121 // - Remove modules required by Drupal core.
danielebarchiesi@4 122 // - Protect against direct circular dependencies.
danielebarchiesi@4 123 // - Remove "intermediate" dependencies.
danielebarchiesi@4 124 $required = drupal_required_modules();
danielebarchiesi@4 125 foreach ($dependencies as $k => $v) {
danielebarchiesi@4 126 if (empty($v) || in_array($v, $required)) {
danielebarchiesi@4 127 unset($dependencies[$k]);
danielebarchiesi@4 128 }
danielebarchiesi@4 129 else {
danielebarchiesi@4 130 $module = features_get_modules($v);
danielebarchiesi@4 131 if ($module && !empty($module->info['dependencies'])) {
danielebarchiesi@4 132 // If this dependency depends on the module itself, we have a circular dependency.
danielebarchiesi@4 133 // Don't let it happen. Only you can prevent forest fires.
danielebarchiesi@4 134 if (in_array($module_name, $module->info['dependencies'])) {
danielebarchiesi@4 135 unset($dependencies[$k]);
danielebarchiesi@4 136 }
danielebarchiesi@4 137 // Iterate through the dependency's dependencies and remove any dependencies
danielebarchiesi@4 138 // that are captured by it.
danielebarchiesi@4 139 else {
danielebarchiesi@4 140 foreach ($module->info['dependencies'] as $j => $dependency) {
danielebarchiesi@4 141 if (array_search($dependency, $dependencies) !== FALSE) {
danielebarchiesi@4 142 $position = array_search($dependency, $dependencies);
danielebarchiesi@4 143 unset($dependencies[$position]);
danielebarchiesi@4 144 }
danielebarchiesi@4 145 }
danielebarchiesi@4 146 }
danielebarchiesi@4 147 }
danielebarchiesi@4 148 }
danielebarchiesi@4 149 }
danielebarchiesi@4 150 return drupal_map_assoc(array_unique($dependencies));
danielebarchiesi@4 151 }
danielebarchiesi@4 152
danielebarchiesi@4 153 /**
danielebarchiesi@4 154 * Iterates over a list of dependencies and maximize the list of modules.
danielebarchiesi@4 155 */
danielebarchiesi@4 156 function _features_export_maximize_dependencies($dependencies, $module_name = '', $maximized = array(), $first = TRUE) {
danielebarchiesi@4 157 foreach ($dependencies as $k => $v) {
danielebarchiesi@4 158 $parsed_dependency = drupal_parse_dependency($v);
danielebarchiesi@4 159 $name = $parsed_dependency['name'];
danielebarchiesi@4 160 if (!in_array($name, $maximized)) {
danielebarchiesi@4 161 $maximized[] = $name;
danielebarchiesi@4 162 $module = features_get_modules($name);
danielebarchiesi@4 163 if ($module && !empty($module->info['dependencies'])) {
danielebarchiesi@4 164 $maximized = array_merge($maximized, _features_export_maximize_dependencies($module->info['dependencies'], $module_name, $maximized, FALSE));
danielebarchiesi@4 165 }
danielebarchiesi@4 166 }
danielebarchiesi@4 167 }
danielebarchiesi@4 168 return array_unique($maximized);
danielebarchiesi@4 169 }
danielebarchiesi@4 170
danielebarchiesi@4 171 /**
danielebarchiesi@4 172 * Prepare a feature export array into a finalized info array.
danielebarchiesi@4 173 * @param $export
danielebarchiesi@4 174 * An exported feature definition.
danielebarchiesi@4 175 * @param $module_name
danielebarchiesi@4 176 * The name of the module to be exported.
danielebarchiesi@4 177 * @param $reset
danielebarchiesi@4 178 * Boolean flag for resetting the module cache. Only set to true when
danielebarchiesi@4 179 * doing a final export for delivery.
danielebarchiesi@4 180 */
danielebarchiesi@4 181 function features_export_prepare($export, $module_name, $reset = FALSE, $add_deprecated = TRUE) {
danielebarchiesi@4 182 $existing = features_get_modules($module_name, $reset);
danielebarchiesi@4 183
danielebarchiesi@4 184 // copy certain exports directly into info
danielebarchiesi@4 185 $copy_list = array('scripts', 'stylesheets');
danielebarchiesi@4 186 foreach ($copy_list as $item) {
danielebarchiesi@4 187 if(isset($export[$item])) {
danielebarchiesi@4 188 $existing->info[$item] = $export[$item];
danielebarchiesi@4 189 }
danielebarchiesi@4 190 }
danielebarchiesi@4 191
danielebarchiesi@4 192 // Prepare info string -- if module exists, merge into its existing info file
danielebarchiesi@4 193 $defaults = !empty($existing->info) ? $existing->info : array('core' => '7.x', 'package' => 'Features');
danielebarchiesi@4 194 $export = array_merge($defaults, $export);
danielebarchiesi@4 195
danielebarchiesi@4 196 $deprecated = features_get_deprecated();
danielebarchiesi@4 197 // Cleanup info array
danielebarchiesi@4 198 foreach ($export['features'] as $component => $data) {
danielebarchiesi@4 199 // if performing the final export, do not export deprecated components
danielebarchiesi@4 200 if (($reset || !$add_deprecated) && !empty($deprecated[$component])) {
danielebarchiesi@4 201 unset($export['features'][$component]);
danielebarchiesi@4 202 }
danielebarchiesi@4 203 else {
danielebarchiesi@4 204 $export['features'][$component] = array_keys($data);
danielebarchiesi@4 205 }
danielebarchiesi@4 206 }
danielebarchiesi@4 207 if (isset($export['dependencies'])) {
danielebarchiesi@4 208 $export['dependencies'] = array_values($export['dependencies']);
danielebarchiesi@4 209 }
danielebarchiesi@4 210 if (isset($export['conflicts'])) {
danielebarchiesi@4 211 unset($export['conflicts']);
danielebarchiesi@4 212 }
danielebarchiesi@4 213
danielebarchiesi@4 214 // Order info array.
danielebarchiesi@4 215 $standard_info = array();
danielebarchiesi@4 216 foreach (array_merge(array('name', 'description', 'core', 'package', 'version', 'project', 'dependencies'), $copy_list) as $item) {
danielebarchiesi@4 217 if (isset($export[$item])) {
danielebarchiesi@4 218 $standard_info[$item] = $export[$item];
danielebarchiesi@4 219 }
danielebarchiesi@4 220 }
danielebarchiesi@4 221 if (isset($export['php']) && ($export['php'] != DRUPAL_MINIMUM_PHP)) {
danielebarchiesi@4 222 $standard_info['php'] = $export['php'];
danielebarchiesi@4 223 }
danielebarchiesi@4 224 unset($export['php']);
danielebarchiesi@4 225
danielebarchiesi@4 226 $export = features_array_diff_assoc_recursive($export, $standard_info);
danielebarchiesi@4 227 ksort($export);
danielebarchiesi@4 228 return array_merge($standard_info, $export);
danielebarchiesi@4 229 }
danielebarchiesi@4 230
danielebarchiesi@4 231 /**
danielebarchiesi@4 232 * Generate an array of hooks and their raw code.
danielebarchiesi@4 233 */
danielebarchiesi@4 234 function features_export_render_hooks($export, $module_name, $reset = FALSE) {
danielebarchiesi@4 235 features_include();
danielebarchiesi@4 236 $code = array();
danielebarchiesi@4 237
danielebarchiesi@4 238 // Sort components to keep exported code consistent
danielebarchiesi@4 239 ksort($export['features']);
danielebarchiesi@4 240
danielebarchiesi@4 241 foreach ($export['features'] as $component => $data) {
danielebarchiesi@4 242 if (!empty($data)) {
danielebarchiesi@4 243 // Sort the items so that we don't generate different exports based on order
danielebarchiesi@4 244 asort($data);
danielebarchiesi@4 245 if (features_hook($component, 'features_export_render')) {
danielebarchiesi@4 246 $hooks = features_invoke($component, 'features_export_render', $module_name, $data, $export);
danielebarchiesi@4 247 $code[$component] = $hooks;
danielebarchiesi@4 248 }
danielebarchiesi@4 249 }
danielebarchiesi@4 250 }
danielebarchiesi@4 251 return $code;
danielebarchiesi@4 252 }
danielebarchiesi@4 253
danielebarchiesi@4 254 /**
danielebarchiesi@4 255 * Render feature export into an array representing its files.
danielebarchiesi@4 256 *
danielebarchiesi@4 257 * @param $export
danielebarchiesi@4 258 * An exported feature definition.
danielebarchiesi@4 259 * @param $module_name
danielebarchiesi@4 260 * The name of the module to be exported.
danielebarchiesi@4 261 * @param $reset
danielebarchiesi@4 262 * Boolean flag for resetting the module cache. Only set to true when
danielebarchiesi@4 263 * doing a final export for delivery.
danielebarchiesi@4 264 *
danielebarchiesi@4 265 * @return array of info file and module file contents.
danielebarchiesi@4 266 */
danielebarchiesi@4 267 function features_export_render($export, $module_name, $reset = FALSE) {
danielebarchiesi@4 268 $code = array();
danielebarchiesi@4 269
danielebarchiesi@4 270 // Generate hook code
danielebarchiesi@4 271 $component_hooks = features_export_render_hooks($export, $module_name, $reset);
danielebarchiesi@4 272 $components = features_get_components();
danielebarchiesi@4 273 $deprecated = features_get_deprecated($components);
danielebarchiesi@4 274
danielebarchiesi@4 275 // Group component code into their respective files
danielebarchiesi@4 276 foreach ($component_hooks as $component => $hooks) {
danielebarchiesi@4 277 if ($reset && !empty($deprecated[$component])) {
danielebarchiesi@4 278 // skip deprecated components on final export
danielebarchiesi@4 279 continue;
danielebarchiesi@4 280 }
danielebarchiesi@4 281 $file = array('name' => 'features');
danielebarchiesi@4 282 if (isset($components[$component]['default_file'])) {
danielebarchiesi@4 283 switch ($components[$component]['default_file']) {
danielebarchiesi@4 284 case FEATURES_DEFAULTS_INCLUDED:
danielebarchiesi@4 285 $file['name'] = "features.$component";
danielebarchiesi@4 286 break;
danielebarchiesi@4 287 case FEATURES_DEFAULTS_CUSTOM:
danielebarchiesi@4 288 $file['name'] = $components[$component]['default_filename'];
danielebarchiesi@4 289 break;
danielebarchiesi@4 290 }
danielebarchiesi@4 291 }
danielebarchiesi@4 292
danielebarchiesi@4 293 if (!isset($code[$file['name']])) {
danielebarchiesi@4 294 $code[$file['name']] = array();
danielebarchiesi@4 295 }
danielebarchiesi@4 296
danielebarchiesi@4 297 foreach ($hooks as $hook_name => $hook_info) {
danielebarchiesi@4 298 $hook_code = is_array($hook_info) ? $hook_info['code'] : $hook_info;
danielebarchiesi@4 299 $hook_args = is_array($hook_info) && !empty($hook_info['args']) ? $hook_info['args'] : '';
danielebarchiesi@4 300 $hook_file = is_array($hook_info) && !empty($hook_info['file']) ? $hook_info['file'] : $file['name'];
danielebarchiesi@4 301 $code[$hook_file][$hook_name] = features_export_render_defaults($module_name, $hook_name, $hook_code, $hook_args);
danielebarchiesi@4 302 }
danielebarchiesi@4 303 }
danielebarchiesi@4 304
danielebarchiesi@4 305 // Finalize strings to be written to files
danielebarchiesi@4 306 $code = array_filter($code);
danielebarchiesi@4 307 foreach ($code as $filename => $contents) {
danielebarchiesi@4 308 $code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
danielebarchiesi@4 309 }
danielebarchiesi@4 310
danielebarchiesi@4 311 // Generate info file output
danielebarchiesi@4 312 $export = features_export_prepare($export, $module_name, $reset);
danielebarchiesi@4 313 $code['info'] = features_export_info($export);
danielebarchiesi@4 314
danielebarchiesi@4 315 // Used to create or manipulate the generated .module for features.inc.
danielebarchiesi@4 316 $modulefile_features_inc = "<?php\n/**\n * @file\n * Code for the {$export['name']} feature.\n */\n\ninclude_once '{$module_name}.features.inc';\n";
danielebarchiesi@4 317 $modulefile_blank = "<?php\n/**\n * @file\n * Drupal needs this blank file.\n */\n";
danielebarchiesi@4 318
danielebarchiesi@4 319 // Prepare the module
danielebarchiesi@4 320 // If module exists, let it be and include it in the files
danielebarchiesi@4 321 if ($existing = features_get_modules($module_name, TRUE)) {
danielebarchiesi@4 322 $code['module'] = file_get_contents($existing->filename);
danielebarchiesi@4 323
danielebarchiesi@4 324 // If the current module file does not reference the features.inc include,
danielebarchiesi@4 325 // @TODO this way of checking does not account for the possibility of inclusion instruction being commented out.
danielebarchiesi@4 326 if (isset($code['features']) && strpos($code['module'], "{$module_name}.features.inc") === FALSE) {
danielebarchiesi@4 327 // If .module does not begin with <?php\n, just add a warning.
danielebarchiesi@4 328 if (strpos($code['module'], "<?php\n") !== 0) {
danielebarchiesi@4 329 features_log(t('@module does not appear to include the @include file.', array('@module' => "{$module_name}.module", '@include' => "{$module_name}.features.inc")), 'warning');
danielebarchiesi@4 330 }
danielebarchiesi@4 331 else {
danielebarchiesi@4 332 // Remove the old message if it exists, else just remove the <?php
danielebarchiesi@4 333 $length = strpos($code['module'], $modulefile_blank) === 0 ? strlen($modulefile_blank) : 6;
danielebarchiesi@4 334 $code['module'] = $modulefile_features_inc . substr($code['module'], $length);
danielebarchiesi@4 335 }
danielebarchiesi@4 336 }
danielebarchiesi@4 337
danielebarchiesi@4 338 if ($reset) {
danielebarchiesi@4 339 // only check for deprecated files on final export
danielebarchiesi@4 340
danielebarchiesi@4 341 // Deprecated files. Display a message for any of these files letting the
danielebarchiesi@4 342 // user know that they may be removed.
danielebarchiesi@4 343 $deprecated_files = array(
danielebarchiesi@4 344 "{$module_name}.defaults",
danielebarchiesi@4 345 "{$module_name}.features.views",
danielebarchiesi@4 346 "{$module_name}.features.node"
danielebarchiesi@4 347 );
danielebarchiesi@4 348 // add deprecated components
danielebarchiesi@4 349 foreach ($deprecated as $component) {
danielebarchiesi@4 350 $info = features_get_components($component);
danielebarchiesi@4 351 $filename = isset($info['default_file']) && $info['default_file'] == FEATURES_DEFAULTS_CUSTOM ? $info['default_filename'] : "features.{$component}";
danielebarchiesi@4 352 $deprecated_files[] = "{$module_name}.$filename";
danielebarchiesi@4 353 }
danielebarchiesi@4 354 foreach (file_scan_directory(drupal_get_path('module', $module_name), '/.*/') as $file) {
danielebarchiesi@4 355 if (in_array($file->name, $deprecated_files, TRUE)) {
danielebarchiesi@4 356 features_log(t('The file @filename has been deprecated and can be removed.', array('@filename' => $file->filename)), 'status');
danielebarchiesi@4 357 }
danielebarchiesi@4 358 elseif ($file->name === "{$module_name}.features" && empty($code['features'])) {
danielebarchiesi@4 359 // Try and remove features.inc include.
danielebarchiesi@4 360 if (strpos($code['module'], "{$module_name}.features.inc")) {
danielebarchiesi@4 361 $code['module'] = str_replace($modulefile_features_inc, $modulefile_blank, $code['module']);
danielebarchiesi@4 362 }
danielebarchiesi@4 363 // If unable to remove the include, add a message to remove.
danielebarchiesi@4 364 if (strpos($code['module'], "{$module_name}.features.inc")) {
danielebarchiesi@4 365 $code['features'] = "<?php\n\n// This file is deprecated and can be removed.\n// Please remove include_once('{$module_name}.features.inc') in {$module_name}.module as well.\n";
danielebarchiesi@4 366 }
danielebarchiesi@4 367 else {
danielebarchiesi@4 368 $code['features'] = "<?php\n\n// This file is deprecated and can be removed.\n";
danielebarchiesi@4 369 }
danielebarchiesi@4 370 }
danielebarchiesi@4 371 }
danielebarchiesi@4 372 }
danielebarchiesi@4 373 }
danielebarchiesi@4 374 // Add a stub module to include the defaults
danielebarchiesi@4 375 else if (!empty($code['features'])) {
danielebarchiesi@4 376 $code['module'] = $modulefile_features_inc;
danielebarchiesi@4 377 }
danielebarchiesi@4 378 else {
danielebarchiesi@4 379 $code['module'] = $modulefile_blank;
danielebarchiesi@4 380 }
danielebarchiesi@4 381 return $code;
danielebarchiesi@4 382 }
danielebarchiesi@4 383
danielebarchiesi@4 384 /**
danielebarchiesi@4 385 * Detect differences between DB and code components of a feature.
danielebarchiesi@4 386 */
danielebarchiesi@4 387 function features_detect_overrides($module) {
danielebarchiesi@4 388 static $cache;
danielebarchiesi@4 389 if (!isset($cache)) {
danielebarchiesi@4 390 $cache = array();
danielebarchiesi@4 391 }
danielebarchiesi@4 392 if (!isset($cache[$module->name])) {
danielebarchiesi@4 393 // Rebuild feature from .info file description and prepare an export from current DB state.
danielebarchiesi@4 394 $export = features_populate($module->info, $module->name);
danielebarchiesi@4 395 $export = features_export_prepare($export, $module->name, FALSE, FALSE);
danielebarchiesi@4 396
danielebarchiesi@4 397 $overridden = array();
danielebarchiesi@4 398
danielebarchiesi@4 399 // Compare feature info
danielebarchiesi@4 400 _features_sanitize($module->info);
danielebarchiesi@4 401 _features_sanitize($export);
danielebarchiesi@4 402
danielebarchiesi@4 403 $compare = array('normal' => features_export_info($export), 'default' => features_export_info($module->info));
danielebarchiesi@4 404 if ($compare['normal'] !== $compare['default']) {
danielebarchiesi@4 405 $overridden['info'] = $compare;
danielebarchiesi@4 406 }
danielebarchiesi@4 407
danielebarchiesi@4 408 // Collect differences at a per-component level
danielebarchiesi@4 409 $states = features_get_component_states(array($module->name), FALSE);
danielebarchiesi@4 410 foreach ($states[$module->name] as $component => $state) {
danielebarchiesi@4 411 if ($state != FEATURES_DEFAULT) {
danielebarchiesi@4 412 $normal = features_get_normal($component, $module->name);
danielebarchiesi@4 413 $default = features_get_default($component, $module->name);
danielebarchiesi@4 414 _features_sanitize($normal);
danielebarchiesi@4 415 _features_sanitize($default);
danielebarchiesi@4 416
danielebarchiesi@4 417 $compare = array('normal' => features_var_export($normal), 'default' => features_var_export($default));
danielebarchiesi@4 418 if (_features_linetrim($compare['normal']) !== _features_linetrim($compare['default'])) {
danielebarchiesi@4 419 $overridden[$component] = $compare;
danielebarchiesi@4 420 }
danielebarchiesi@4 421 }
danielebarchiesi@4 422 }
danielebarchiesi@4 423 $cache[$module->name] = $overridden;
danielebarchiesi@4 424 }
danielebarchiesi@4 425 return $cache[$module->name];
danielebarchiesi@4 426 }
danielebarchiesi@4 427
danielebarchiesi@4 428 /**
danielebarchiesi@4 429 * Gets the available default hooks keyed by components.
danielebarchiesi@4 430 */
danielebarchiesi@4 431 function features_get_default_hooks($component = NULL, $reset = FALSE) {
danielebarchiesi@4 432 return features_get_components($component, 'default_hook', $reset);
danielebarchiesi@4 433 }
danielebarchiesi@4 434
danielebarchiesi@4 435 /**
danielebarchiesi@4 436 * Gets the available default hooks keyed by components.
danielebarchiesi@4 437 */
danielebarchiesi@4 438 function features_get_default_alter_hook($component) {
danielebarchiesi@4 439 $default_hook = features_get_components($component, 'default_hook');
danielebarchiesi@4 440 $alter_hook = features_get_components($component, 'alter_hook');
danielebarchiesi@4 441 $alter_type = features_get_components($component, 'alter_type');
danielebarchiesi@4 442 return empty($alter_type) || $alter_type != 'none' ? ($alter_hook ? $alter_hook : $default_hook) : FALSE;
danielebarchiesi@4 443 }
danielebarchiesi@4 444
danielebarchiesi@4 445 /**
danielebarchiesi@4 446 * Return a code string representing an implementation of a defaults module hook.
danielebarchiesi@4 447 */
danielebarchiesi@4 448 function features_export_render_defaults($module, $hook, $code, $args = '') {
danielebarchiesi@4 449 $output = array();
danielebarchiesi@4 450 $output[] = "/**";
danielebarchiesi@4 451 $output[] = " * Implements hook_{$hook}().";
danielebarchiesi@4 452 $output[] = " */";
danielebarchiesi@4 453 $output[] = "function {$module}_{$hook}(" . $args . ") {";
danielebarchiesi@4 454 $output[] = $code;
danielebarchiesi@4 455 $output[] = "}";
danielebarchiesi@4 456 return implode("\n", $output);
danielebarchiesi@4 457 }
danielebarchiesi@4 458
danielebarchiesi@4 459 /**
danielebarchiesi@4 460 * Generate code friendly to the Drupal .info format from a structured array.
danielebarchiesi@4 461 *
danielebarchiesi@4 462 * @param $info
danielebarchiesi@4 463 * An array or single value to put in a module's .info file.
danielebarchiesi@4 464 * @param $parents
danielebarchiesi@4 465 * Array of parent keys (internal use only).
danielebarchiesi@4 466 *
danielebarchiesi@4 467 * @return
danielebarchiesi@4 468 * A code string ready to be written to a module's .info file.
danielebarchiesi@4 469 */
danielebarchiesi@4 470 function features_export_info($info, $parents = array()) {
danielebarchiesi@4 471 $output = '';
danielebarchiesi@4 472 if (is_array($info)) {
danielebarchiesi@4 473 foreach ($info as $k => $v) {
danielebarchiesi@4 474 $child = $parents;
danielebarchiesi@4 475 $child[] = $k;
danielebarchiesi@4 476 $output .= features_export_info($v, $child);
danielebarchiesi@4 477 }
danielebarchiesi@4 478 }
danielebarchiesi@4 479 else if (!empty($info) && count($parents)) {
danielebarchiesi@4 480 $line = array_shift($parents);
danielebarchiesi@4 481 foreach ($parents as $key) {
danielebarchiesi@4 482 $line .= is_numeric($key) ? "[]" : "[{$key}]";
danielebarchiesi@4 483 }
danielebarchiesi@4 484 $line .= " = {$info}\n";
danielebarchiesi@4 485 return $line;
danielebarchiesi@4 486 }
danielebarchiesi@4 487 return $output;
danielebarchiesi@4 488 }
danielebarchiesi@4 489
danielebarchiesi@4 490 /**
danielebarchiesi@4 491 * Tar creation function. Written by dmitrig01.
danielebarchiesi@4 492 *
danielebarchiesi@4 493 * @param $name
danielebarchiesi@4 494 * Filename of the file to be tarred.
danielebarchiesi@4 495 * @param $contents
danielebarchiesi@4 496 * String contents of the file.
danielebarchiesi@4 497 *
danielebarchiesi@4 498 * @return
danielebarchiesi@4 499 * A string of the tar file contents.
danielebarchiesi@4 500 */
danielebarchiesi@4 501 function features_tar_create($name, $contents) {
danielebarchiesi@4 502 /* http://www.mkssoftware.com/docs/man4/tar.4.asp */
danielebarchiesi@4 503 /* http://www.phpclasses.org/browse/file/21200.html */
danielebarchiesi@4 504 $tar = '';
danielebarchiesi@4 505 $bigheader = $header = '';
danielebarchiesi@4 506 if (strlen($name) > 100) {
danielebarchiesi@4 507 $bigheader = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12",
danielebarchiesi@4 508 '././@LongLink', '0000000', '0000000', '0000000',
danielebarchiesi@4 509 sprintf("%011o", strlen($name)), '00000000000',
danielebarchiesi@4 510 ' ', 'L', '', 'ustar ', '0',
danielebarchiesi@4 511 '', '', '', '', '', '');
danielebarchiesi@4 512
danielebarchiesi@4 513 $bigheader .= str_pad($name, floor((strlen($name) + 512 - 1) / 512) * 512, "\0");
danielebarchiesi@4 514
danielebarchiesi@4 515 $checksum = 0;
danielebarchiesi@4 516 for ($i = 0; $i < 512; $i++) {
danielebarchiesi@4 517 $checksum += ord(substr($bigheader, $i, 1));
danielebarchiesi@4 518 }
danielebarchiesi@4 519 $bigheader = substr_replace($bigheader, sprintf("%06o", $checksum)."\0 ", 148, 8);
danielebarchiesi@4 520 }
danielebarchiesi@4 521 $header = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12", // book the memorie area
danielebarchiesi@4 522 substr($name,0,100), // 0 100 File name
danielebarchiesi@4 523 '100644 ', // File permissions
danielebarchiesi@4 524 ' 765 ', // UID,
danielebarchiesi@4 525 ' 765 ', // GID,
danielebarchiesi@4 526 sprintf("%11s ", decoct(strlen($contents))), // Filesize,
danielebarchiesi@4 527 sprintf("%11s", decoct(REQUEST_TIME)), // Creation time
danielebarchiesi@4 528 ' ', // 148 8 Check sum for header block
danielebarchiesi@4 529 '', // 156 1 Link indicator / ustar Type flag
danielebarchiesi@4 530 '', // 157 100 Name of linked file
danielebarchiesi@4 531 'ustar ', // 257 6 USTAR indicator "ustar"
danielebarchiesi@4 532 ' ', // 263 2 USTAR version "00"
danielebarchiesi@4 533 '', // 265 32 Owner user name
danielebarchiesi@4 534 '', // 297 32 Owner group name
danielebarchiesi@4 535 '', // 329 8 Device major number
danielebarchiesi@4 536 '', // 337 8 Device minor number
danielebarchiesi@4 537 '', // 345 155 Filename prefix
danielebarchiesi@4 538 ''); // 500 12 ??
danielebarchiesi@4 539
danielebarchiesi@4 540 $checksum = 0;
danielebarchiesi@4 541 for ($i = 0; $i < 512; $i++) {
danielebarchiesi@4 542 $checksum += ord(substr($header, $i, 1));
danielebarchiesi@4 543 }
danielebarchiesi@4 544 $header = substr_replace($header, sprintf("%06o", $checksum)."\0 ", 148, 8);
danielebarchiesi@4 545 $tar = $bigheader.$header;
danielebarchiesi@4 546
danielebarchiesi@4 547 $buffer = str_split($contents, 512);
danielebarchiesi@4 548 foreach ($buffer as $item) {
danielebarchiesi@4 549 $tar .= pack("a512", $item);
danielebarchiesi@4 550 }
danielebarchiesi@4 551 return $tar;
danielebarchiesi@4 552 }
danielebarchiesi@4 553
danielebarchiesi@4 554 /**
danielebarchiesi@4 555 * Export var function -- from Views.
danielebarchiesi@4 556 */
danielebarchiesi@4 557 function features_var_export($var, $prefix = '', $init = TRUE, $count = 0) {
danielebarchiesi@4 558 if ($count > 50) {
danielebarchiesi@4 559 watchdog('features', 'Recursion depth reached in features_var_export', array());
danielebarchiesi@4 560 return '';
danielebarchiesi@4 561 }
danielebarchiesi@4 562
danielebarchiesi@4 563 if (is_object($var)) {
danielebarchiesi@4 564 $output = method_exists($var, 'export') ? $var->export() : features_var_export((array) $var, '', FALSE, $count+1);
danielebarchiesi@4 565 }
danielebarchiesi@4 566 else if (is_array($var)) {
danielebarchiesi@4 567 if (empty($var)) {
danielebarchiesi@4 568 $output = 'array()';
danielebarchiesi@4 569 }
danielebarchiesi@4 570 else {
danielebarchiesi@4 571 $output = "array(\n";
danielebarchiesi@4 572 foreach ($var as $key => $value) {
danielebarchiesi@4 573 // Using normal var_export on the key to ensure correct quoting.
danielebarchiesi@4 574 $output .= " " . var_export($key, TRUE) . " => " . features_var_export($value, ' ', FALSE, $count+1) . ",\n";
danielebarchiesi@4 575 }
danielebarchiesi@4 576 $output .= ')';
danielebarchiesi@4 577 }
danielebarchiesi@4 578 }
danielebarchiesi@4 579 else if (is_bool($var)) {
danielebarchiesi@4 580 $output = $var ? 'TRUE' : 'FALSE';
danielebarchiesi@4 581 }
danielebarchiesi@4 582 else if (is_int($var)) {
danielebarchiesi@4 583 $output = intval($var);
danielebarchiesi@4 584 }
danielebarchiesi@4 585 else if (is_numeric($var)) {
danielebarchiesi@4 586 $output = floatval($var);
danielebarchiesi@4 587 }
danielebarchiesi@4 588 else if (is_string($var) && strpos($var, "\n") !== FALSE) {
danielebarchiesi@4 589 // Replace line breaks in strings with a token for replacement
danielebarchiesi@4 590 // at the very end. This protects whitespace in strings from
danielebarchiesi@4 591 // unintentional indentation.
danielebarchiesi@4 592 $var = str_replace("\n", "***BREAK***", $var);
danielebarchiesi@4 593 $output = var_export($var, TRUE);
danielebarchiesi@4 594 }
danielebarchiesi@4 595 else {
danielebarchiesi@4 596 $output = var_export($var, TRUE);
danielebarchiesi@4 597 }
danielebarchiesi@4 598
danielebarchiesi@4 599 if ($prefix) {
danielebarchiesi@4 600 $output = str_replace("\n", "\n$prefix", $output);
danielebarchiesi@4 601 }
danielebarchiesi@4 602
danielebarchiesi@4 603 if ($init) {
danielebarchiesi@4 604 $output = str_replace("***BREAK***", "\n", $output);
danielebarchiesi@4 605 }
danielebarchiesi@4 606
danielebarchiesi@4 607 return $output;
danielebarchiesi@4 608 }
danielebarchiesi@4 609
danielebarchiesi@4 610 /**
danielebarchiesi@4 611 * Helper function to return an array of t()'d translatables strings.
danielebarchiesi@4 612 * Useful for providing a separate array of translatables with your
danielebarchiesi@4 613 * export so that string extractors like potx can detect them.
danielebarchiesi@4 614 */
danielebarchiesi@4 615 function features_translatables_export($translatables, $indent = '') {
danielebarchiesi@4 616 $output = '';
danielebarchiesi@4 617 $translatables = array_filter(array_unique($translatables));
danielebarchiesi@4 618 if (!empty($translatables)) {
danielebarchiesi@4 619 $output .= "{$indent}// Translatables\n";
danielebarchiesi@4 620 $output .= "{$indent}// Included for use with string extractors like potx.\n";
danielebarchiesi@4 621 sort($translatables);
danielebarchiesi@4 622 foreach ($translatables as $string) {
danielebarchiesi@4 623 $output .= "{$indent}t(" . features_var_export($string) . ");\n";
danielebarchiesi@4 624 }
danielebarchiesi@4 625 }
danielebarchiesi@4 626 return $output;
danielebarchiesi@4 627 }
danielebarchiesi@4 628
danielebarchiesi@4 629 /**
danielebarchiesi@4 630 * Get a summary storage state for a feature.
danielebarchiesi@4 631 */
danielebarchiesi@4 632 function features_get_storage($module_name) {
danielebarchiesi@4 633 // Get component states, and array_diff against array(FEATURES_DEFAULT).
danielebarchiesi@4 634 // If the returned array has any states that don't match FEATURES_DEFAULT,
danielebarchiesi@4 635 // return the highest state.
danielebarchiesi@4 636 $states = features_get_component_states(array($module_name), FALSE);
danielebarchiesi@4 637 $states = array_diff($states[$module_name], array(FEATURES_DEFAULT));
danielebarchiesi@4 638 $storage = !empty($states) ? max($states) : FEATURES_DEFAULT;
danielebarchiesi@4 639 return $storage;
danielebarchiesi@4 640 }
danielebarchiesi@4 641
danielebarchiesi@4 642 /**
danielebarchiesi@4 643 * Wrapper around features_get_[storage] to return an md5hash of a normalized
danielebarchiesi@4 644 * defaults/normal object array. Can be used to compare normal/default states
danielebarchiesi@4 645 * of a module's component.
danielebarchiesi@4 646 */
danielebarchiesi@4 647 function features_get_signature($state = 'default', $module_name, $component, $reset = FALSE) {
danielebarchiesi@4 648 switch ($state) {
danielebarchiesi@4 649 case 'cache':
danielebarchiesi@4 650 $codecache = variable_get('features_codecache', array());
danielebarchiesi@4 651 return isset($codecache[$module_name][$component]) ? $codecache[$module_name][$component] : FALSE;
danielebarchiesi@4 652 case 'default':
danielebarchiesi@4 653 $objects = features_get_default($component, $module_name, TRUE, $reset);
danielebarchiesi@4 654 break;
danielebarchiesi@4 655 case 'normal':
danielebarchiesi@4 656 $objects = features_get_normal($component, $module_name, $reset);
danielebarchiesi@4 657 break;
danielebarchiesi@4 658 }
danielebarchiesi@4 659 if (!empty($objects)) {
danielebarchiesi@4 660 $objects = (array) $objects;
danielebarchiesi@4 661 _features_sanitize($objects);
danielebarchiesi@4 662 return md5(_features_linetrim(features_var_export($objects)));
danielebarchiesi@4 663 }
danielebarchiesi@4 664 return FALSE;
danielebarchiesi@4 665 }
danielebarchiesi@4 666
danielebarchiesi@4 667 /**
danielebarchiesi@4 668 * Set the signature of a module/component pair in the codecache.
danielebarchiesi@4 669 */
danielebarchiesi@4 670 function features_set_signature($module, $component, $signature = NULL) {
danielebarchiesi@4 671 $var_codecache = variable_get('features_codecache', array());
danielebarchiesi@4 672 $signature = isset($signature) ? $signature : features_get_signature('default', $module, $component, TRUE);
danielebarchiesi@4 673 $var_codecache[$module][$component] = $signature;
danielebarchiesi@4 674 variable_set('features_codecache', $var_codecache);
danielebarchiesi@4 675 }
danielebarchiesi@4 676
danielebarchiesi@4 677 /**
danielebarchiesi@4 678 * Processing semaphore operations.
danielebarchiesi@4 679 */
danielebarchiesi@4 680 function features_semaphore($op, $component) {
danielebarchiesi@4 681 // Note: we don't use variable_get() here as the inited variable
danielebarchiesi@4 682 // static cache may be stale. Retrieving directly from the DB narrows
danielebarchiesi@4 683 // the possibility of collision.
danielebarchiesi@4 684 $semaphore = db_query("SELECT value FROM {variable} WHERE name = :name", array(':name' => 'features_semaphore'))->fetchField();
danielebarchiesi@4 685 $semaphore = !empty($semaphore) ? unserialize($semaphore) : array();
danielebarchiesi@4 686
danielebarchiesi@4 687 switch ($op) {
danielebarchiesi@4 688 case 'get':
danielebarchiesi@4 689 return isset($semaphore[$component]) ? $semaphore[$component] : FALSE;
danielebarchiesi@4 690 case 'set':
danielebarchiesi@4 691 $semaphore[$component] = REQUEST_TIME;
danielebarchiesi@4 692 variable_set('features_semaphore', $semaphore);
danielebarchiesi@4 693 break;
danielebarchiesi@4 694 case 'del':
danielebarchiesi@4 695 if (isset($semaphore[$component])) {
danielebarchiesi@4 696 unset($semaphore[$component]);
danielebarchiesi@4 697 variable_set('features_semaphore', $semaphore);
danielebarchiesi@4 698 }
danielebarchiesi@4 699 break;
danielebarchiesi@4 700 }
danielebarchiesi@4 701 }
danielebarchiesi@4 702
danielebarchiesi@4 703 /**
danielebarchiesi@4 704 * Get normal objects for a given module/component pair.
danielebarchiesi@4 705 */
danielebarchiesi@4 706 function features_get_normal($component, $module_name, $reset = FALSE) {
danielebarchiesi@4 707 static $cache;
danielebarchiesi@4 708 if (!isset($cache) || $reset) {
danielebarchiesi@4 709 $cache = array();
danielebarchiesi@4 710 }
danielebarchiesi@4 711 if (!isset($cache[$module_name][$component])) {
danielebarchiesi@4 712 features_include();
danielebarchiesi@4 713 $code = NULL;
danielebarchiesi@4 714 $module = features_get_features($module_name);
danielebarchiesi@4 715
danielebarchiesi@4 716 // Special handling for dependencies component.
danielebarchiesi@4 717 if ($component === 'dependencies') {
danielebarchiesi@4 718 $cache[$module_name][$component] = isset($module->info['dependencies']) ? array_filter($module->info['dependencies'], 'module_exists') : array();
danielebarchiesi@4 719 }
danielebarchiesi@4 720 // All other components.
danielebarchiesi@4 721 else {
danielebarchiesi@4 722 $default_hook = features_get_default_hooks($component);
danielebarchiesi@4 723 if ($module && $default_hook && isset($module->info['features'][$component]) && features_hook($component, 'features_export_render')) {
danielebarchiesi@4 724 $code = features_invoke($component, 'features_export_render', $module_name, $module->info['features'][$component], NULL);
danielebarchiesi@4 725 $cache[$module_name][$component] = isset($code[$default_hook]) ? eval($code[$default_hook]) : FALSE;
danielebarchiesi@4 726 }
danielebarchiesi@4 727 }
danielebarchiesi@4 728
danielebarchiesi@4 729 // Clear out vars for memory's sake.
danielebarchiesi@4 730 unset($code);
danielebarchiesi@4 731 unset($module);
danielebarchiesi@4 732 }
danielebarchiesi@4 733 return isset($cache[$module_name][$component]) ? $cache[$module_name][$component] : FALSE;
danielebarchiesi@4 734 }
danielebarchiesi@4 735
danielebarchiesi@4 736 /**
danielebarchiesi@4 737 * Get defaults for a given module/component pair.
danielebarchiesi@4 738 */
danielebarchiesi@4 739 function features_get_default($component, $module_name = NULL, $alter = TRUE, $reset = FALSE) {
danielebarchiesi@4 740 static $cache = array();
danielebarchiesi@4 741 $alter = !empty($alter); // ensure $alter is a true/false boolean
danielebarchiesi@4 742 features_include();
danielebarchiesi@4 743 features_include_defaults($component);
danielebarchiesi@4 744 $default_hook = features_get_default_hooks($component);
danielebarchiesi@4 745 $components = features_get_components();
danielebarchiesi@4 746
danielebarchiesi@4 747 // Collect defaults for all modules if no module name was specified.
danielebarchiesi@4 748 if (isset($module_name)) {
danielebarchiesi@4 749 $modules = array($module_name);
danielebarchiesi@4 750 }
danielebarchiesi@4 751 else {
danielebarchiesi@4 752 if ($component === 'dependencies') {
danielebarchiesi@4 753 $modules = array_keys(features_get_features());
danielebarchiesi@4 754 }
danielebarchiesi@4 755 else {
danielebarchiesi@4 756 $modules = array();
danielebarchiesi@4 757 foreach (features_get_component_map($component) as $component_modules) {
danielebarchiesi@4 758 $modules = array_merge($modules, $component_modules);
danielebarchiesi@4 759 }
danielebarchiesi@4 760 $modules = array_unique($modules);
danielebarchiesi@4 761 }
danielebarchiesi@4 762 }
danielebarchiesi@4 763
danielebarchiesi@4 764 // Collect and cache information for each specified module.
danielebarchiesi@4 765 foreach ($modules as $m) {
danielebarchiesi@4 766 if (!isset($cache[$component][$alter][$m]) || $reset) {
danielebarchiesi@4 767 // Special handling for dependencies component.
danielebarchiesi@4 768 if ($component === 'dependencies') {
danielebarchiesi@4 769 $module = features_get_features($m);
danielebarchiesi@4 770 $cache[$component][$alter][$m] = isset($module->info['dependencies']) ? $module->info['dependencies'] : array();
danielebarchiesi@4 771 unset($module);
danielebarchiesi@4 772 }
danielebarchiesi@4 773 // All other components
danielebarchiesi@4 774 else {
danielebarchiesi@4 775 if ($default_hook && module_hook($m, $default_hook)) {
danielebarchiesi@4 776 $cache[$component][$alter][$m] = call_user_func("{$m}_{$default_hook}");
danielebarchiesi@4 777 if (is_array($cache[$component][$alter][$m])) {
danielebarchiesi@4 778 $alter_type = features_get_components('alter_type', $component);
danielebarchiesi@4 779 if ($alter && (!isset($alter_type) || $alter_type == FEATURES_ALTER_TYPE_NORMAL)) {
danielebarchiesi@4 780 if ($alter_hook = features_get_default_alter_hook($component)) {
danielebarchiesi@4 781 drupal_alter($alter_hook, $cache[$component][$alter][$m]);
danielebarchiesi@4 782 }
danielebarchiesi@4 783 }
danielebarchiesi@4 784 }
danielebarchiesi@4 785 else {
danielebarchiesi@4 786 $cache[$component][$alter][$m] = FALSE;
danielebarchiesi@4 787 }
danielebarchiesi@4 788 }
danielebarchiesi@4 789 else {
danielebarchiesi@4 790 $cache[$component][$alter][$m] = FALSE;
danielebarchiesi@4 791 }
danielebarchiesi@4 792 }
danielebarchiesi@4 793 }
danielebarchiesi@4 794 }
danielebarchiesi@4 795
danielebarchiesi@4 796 // A specific module was specified. Retrieve only its components.
danielebarchiesi@4 797 if (isset($module_name)) {
danielebarchiesi@4 798 return isset($cache[$component][$alter][$module_name]) ? $cache[$component][$alter][$module_name] : FALSE;
danielebarchiesi@4 799 }
danielebarchiesi@4 800 // No module was specified. Retrieve all components.
danielebarchiesi@4 801 $all_defaults = array();
danielebarchiesi@4 802 if (isset($cache[$component][$alter])) {
danielebarchiesi@4 803 foreach (array_filter($cache[$component][$alter]) as $module_components) {
danielebarchiesi@4 804 $all_defaults = array_merge($all_defaults, $module_components);
danielebarchiesi@4 805 }
danielebarchiesi@4 806 }
danielebarchiesi@4 807 return $all_defaults;
danielebarchiesi@4 808 }
danielebarchiesi@4 809
danielebarchiesi@4 810 /**
danielebarchiesi@4 811 * Get a map of components to their providing modules.
danielebarchiesi@4 812 */
danielebarchiesi@4 813 function features_get_default_map($component, $attribute = NULL, $callback = NULL, $reset = FALSE) {
danielebarchiesi@4 814 static $map = array();
danielebarchiesi@4 815
danielebarchiesi@4 816 global $features_ignore_conflicts;
danielebarchiesi@4 817 if ($features_ignore_conflicts) {
danielebarchiesi@4 818 return FALSE;
danielebarchiesi@4 819 }
danielebarchiesi@4 820
danielebarchiesi@4 821 features_include();
danielebarchiesi@4 822 features_include_defaults($component);
danielebarchiesi@4 823 if ((!isset($map[$component]) || $reset) && $default_hook = features_get_default_hooks($component)) {
danielebarchiesi@4 824 $map[$component] = array();
danielebarchiesi@4 825 foreach (module_implements($default_hook) as $module) {
danielebarchiesi@4 826 if ($defaults = features_get_default($component, $module)) {
danielebarchiesi@4 827 foreach ($defaults as $key => $object) {
danielebarchiesi@4 828 if (isset($callback)) {
danielebarchiesi@4 829 if ($object_key = $callback($object)) {
danielebarchiesi@4 830 $map[$component][$object_key] = $module;
danielebarchiesi@4 831 }
danielebarchiesi@4 832 }
danielebarchiesi@4 833 elseif (isset($attribute)) {
danielebarchiesi@4 834 if (is_object($object) && isset($object->{$attribute})) {
danielebarchiesi@4 835 $map[$component][$object->{$attribute}] = $module;
danielebarchiesi@4 836 }
danielebarchiesi@4 837 elseif (is_array($object) && isset($object[$attribute])) {
danielebarchiesi@4 838 $map[$component][$object[$attribute]] = $module;
danielebarchiesi@4 839 }
danielebarchiesi@4 840 }
danielebarchiesi@4 841 elseif (!isset($attribute) && !isset($callback)) {
danielebarchiesi@4 842 if (!is_numeric($key)) {
danielebarchiesi@4 843 $map[$component][$key] = $module;
danielebarchiesi@4 844 }
danielebarchiesi@4 845 }
danielebarchiesi@4 846 else {
danielebarchiesi@4 847 return FALSE;
danielebarchiesi@4 848 }
danielebarchiesi@4 849 }
danielebarchiesi@4 850 }
danielebarchiesi@4 851 }
danielebarchiesi@4 852 }
danielebarchiesi@4 853 return isset($map[$component]) ? $map[$component] : FALSE;
danielebarchiesi@4 854 }
danielebarchiesi@4 855
danielebarchiesi@4 856 /**
danielebarchiesi@4 857 * Retrieve an array of features/components and their current states.
danielebarchiesi@4 858 */
danielebarchiesi@4 859 function features_get_component_states($features = array(), $rebuild_only = TRUE, $reset = FALSE) {
danielebarchiesi@4 860 static $cache;
danielebarchiesi@4 861 if (!isset($cache) || $reset) {
danielebarchiesi@4 862 $cache = array();
danielebarchiesi@4 863 }
danielebarchiesi@4 864
danielebarchiesi@4 865 $features = !empty($features) ? $features : array_keys(features_get_features());
danielebarchiesi@4 866
danielebarchiesi@4 867 // Retrieve only rebuildable components if requested.
danielebarchiesi@4 868 features_include();
danielebarchiesi@4 869 $components = array_keys(features_get_components());
danielebarchiesi@4 870 if ($rebuild_only) {
danielebarchiesi@4 871 foreach ($components as $k => $component) {
danielebarchiesi@4 872 if (!features_hook($component, 'features_rebuild')) {
danielebarchiesi@4 873 unset($components[$k]);
danielebarchiesi@4 874 }
danielebarchiesi@4 875 }
danielebarchiesi@4 876 }
danielebarchiesi@4 877
danielebarchiesi@4 878 foreach ($features as $feature) {
danielebarchiesi@4 879 $cache[$feature] = isset($cache[$feature]) ? $cache[$feature] : array();
danielebarchiesi@4 880 if (module_exists($feature)) {
danielebarchiesi@4 881 foreach ($components as $component) {
danielebarchiesi@4 882 if (!isset($cache[$feature][$component])) {
danielebarchiesi@4 883 $normal = features_get_signature('normal', $feature, $component, $reset);
danielebarchiesi@4 884 $default = features_get_signature('default', $feature, $component, $reset);
danielebarchiesi@4 885 $codecache = features_get_signature('cache', $feature, $component, $reset);
danielebarchiesi@4 886 $semaphore = features_semaphore('get', $component);
danielebarchiesi@4 887
danielebarchiesi@4 888 // DB and code states match, there is nothing more to check.
danielebarchiesi@4 889 if ($normal == $default) {
danielebarchiesi@4 890 $cache[$feature][$component] = FEATURES_DEFAULT;
danielebarchiesi@4 891
danielebarchiesi@4 892 // Stale semaphores can be deleted.
danielebarchiesi@4 893 features_semaphore('del', $component);
danielebarchiesi@4 894
danielebarchiesi@4 895 // Update code cache if it is stale, clear out semaphore if it stale.
danielebarchiesi@4 896 if ($default != $codecache) {
danielebarchiesi@4 897 features_set_signature($feature, $component, $default);
danielebarchiesi@4 898 }
danielebarchiesi@4 899 }
danielebarchiesi@4 900 // Component properly implements exportables.
danielebarchiesi@4 901 else if (!features_hook($component, 'features_rebuild')) {
danielebarchiesi@4 902 $cache[$feature][$component] = FEATURES_OVERRIDDEN;
danielebarchiesi@4 903 }
danielebarchiesi@4 904 // Component does not implement exportables.
danielebarchiesi@4 905 else {
danielebarchiesi@4 906 if (empty($semaphore)) {
danielebarchiesi@4 907 // Exception for dependencies. Dependencies are always rebuildable.
danielebarchiesi@4 908 if ($component === 'dependencies') {
danielebarchiesi@4 909 $cache[$feature][$component] = FEATURES_REBUILDABLE;
danielebarchiesi@4 910 }
danielebarchiesi@4 911 // All other rebuildable components require comparison.
danielebarchiesi@4 912 else {
danielebarchiesi@4 913 // Code has not changed, but DB does not match. User has DB overrides.
danielebarchiesi@4 914 if ($codecache == $default) {
danielebarchiesi@4 915 $cache[$feature][$component] = FEATURES_OVERRIDDEN;
danielebarchiesi@4 916 }
danielebarchiesi@4 917 // DB has no modifications to prior code state (or this is initial install).
danielebarchiesi@4 918 else if (empty($codecache) || $codecache == $normal) {
danielebarchiesi@4 919 $cache[$feature][$component] = FEATURES_REBUILDABLE;
danielebarchiesi@4 920 }
danielebarchiesi@4 921 // None of the states match. Requires user intervention.
danielebarchiesi@4 922 else if ($codecache != $default) {
danielebarchiesi@4 923 $cache[$feature][$component] = FEATURES_NEEDS_REVIEW;
danielebarchiesi@4 924 }
danielebarchiesi@4 925 }
danielebarchiesi@4 926 }
danielebarchiesi@4 927 else {
danielebarchiesi@4 928 // Semaphore is still within processing horizon. Do nothing.
danielebarchiesi@4 929 if ((REQUEST_TIME - $semaphore) < FEATURES_SEMAPHORE_TIMEOUT) {
danielebarchiesi@4 930 $cache[$feature][$component] = FEATURES_REBUILDING;
danielebarchiesi@4 931 }
danielebarchiesi@4 932 // A stale semaphore means a previous rebuild attempt did not complete.
danielebarchiesi@4 933 // Attempt to complete the rebuild.
danielebarchiesi@4 934 else {
danielebarchiesi@4 935 $cache[$feature][$component] = FEATURES_REBUILDABLE;
danielebarchiesi@4 936 }
danielebarchiesi@4 937 }
danielebarchiesi@4 938 }
danielebarchiesi@4 939 }
danielebarchiesi@4 940 }
danielebarchiesi@4 941 }
danielebarchiesi@4 942 }
danielebarchiesi@4 943
danielebarchiesi@4 944 // Filter cached components on the way out to ensure that even if we have
danielebarchiesi@4 945 // cached more data than has been requested, the return value only reflects
danielebarchiesi@4 946 // the requested features/components.
danielebarchiesi@4 947 $return = $cache;
danielebarchiesi@4 948 $return = array_intersect_key($return, array_flip($features));
danielebarchiesi@4 949 foreach ($return as $k => $v) {
danielebarchiesi@4 950 $return[$k] = array_intersect_key($return[$k], array_flip($components));
danielebarchiesi@4 951 }
danielebarchiesi@4 952 return $return;
danielebarchiesi@4 953 }
danielebarchiesi@4 954
danielebarchiesi@4 955 /**
danielebarchiesi@4 956 * Helper function to eliminate whitespace differences in code.
danielebarchiesi@4 957 */
danielebarchiesi@4 958 function _features_linetrim($code) {
danielebarchiesi@4 959 $code = explode("\n", $code);
danielebarchiesi@4 960 foreach ($code as $k => $line) {
danielebarchiesi@4 961 $code[$k] = trim($line);
danielebarchiesi@4 962 }
danielebarchiesi@4 963 return implode("\n", $code);
danielebarchiesi@4 964 }
danielebarchiesi@4 965
danielebarchiesi@4 966 /**
danielebarchiesi@4 967 * "Sanitizes" an array recursively, performing two key operations:
danielebarchiesi@4 968 * - Sort an array by its keys (assoc) or values (non-assoc)
danielebarchiesi@4 969 * - Remove any null or empty values for associative arrays (array_filter()).
danielebarchiesi@4 970 */
danielebarchiesi@4 971 function _features_sanitize(&$array) {
danielebarchiesi@4 972 if (is_array($array)) {
danielebarchiesi@4 973 $is_assoc = _features_is_assoc($array);
danielebarchiesi@4 974 if ($is_assoc) {
danielebarchiesi@4 975 ksort($array, SORT_STRING);
danielebarchiesi@4 976 $array = array_filter($array);
danielebarchiesi@4 977 }
danielebarchiesi@4 978 else {
danielebarchiesi@4 979 sort($array);
danielebarchiesi@4 980 }
danielebarchiesi@4 981 foreach ($array as $k => $v) {
danielebarchiesi@4 982 if (is_array($v)) {
danielebarchiesi@4 983 _features_sanitize($array[$k]);
danielebarchiesi@4 984 if ($is_assoc && empty($array[$k])) {
danielebarchiesi@4 985 unset($array[$k]);
danielebarchiesi@4 986 }
danielebarchiesi@4 987 }
danielebarchiesi@4 988 }
danielebarchiesi@4 989 }
danielebarchiesi@4 990 }
danielebarchiesi@4 991
danielebarchiesi@4 992 /**
danielebarchiesi@4 993 * Is the given array an associative array. This basically extracts the keys twice to get the
danielebarchiesi@4 994 * numerically ordered keys. It then does a diff with the original array and if there is no
danielebarchiesi@4 995 * key diff then the original array is not associative.
danielebarchiesi@4 996 *
danielebarchiesi@4 997 * NOTE: If you have non-sequential numerical keys, this will identify the array as assoc.
danielebarchiesi@4 998 *
danielebarchiesi@4 999 * Borrowed from: http://www.php.net/manual/en/function.is-array.php#96724
danielebarchiesi@4 1000 *
danielebarchiesi@4 1001 * @return True is the array is an associative array, false otherwise
danielebarchiesi@4 1002 */
danielebarchiesi@4 1003 function _features_is_assoc($array) {
danielebarchiesi@4 1004 return (is_array($array) && (0 !== count(array_diff_key($array, array_keys(array_keys($array)))) || count($array)==0));
danielebarchiesi@4 1005 }