| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: features.admin.inc,v 1.1.2.68 2010/08/20 14:51:38 yhahn Exp $ 3 4 /** 5 * Form callback for features export form. Acts as a router based on the form_state. 6 */ 7 function features_export_form($form_state, $feature = NULL) { 8 module_load_include('inc', 'features', 'features.export'); 9 features_include(); 10 11 $form = array( 12 '#attributes' => array('class' => 'features-export-form'), 13 '#feature' => isset($feature) ? $feature : NULL, 14 ); 15 $form['info'] = array( 16 '#type' => 'fieldset', 17 '#tree' => FALSE, 18 ); 19 $form['info']['name'] = array( 20 '#title' => t('Name'), 21 '#description' => t('Example: Image gallery'), 22 '#type' => 'textfield', 23 '#required' => TRUE, 24 '#default_value' => !empty($feature->info['name']) ? $feature->info['name'] : '', 25 '#attributes' => array('class' => 'feature-name'), 26 ); 27 $form['info']['module_name'] = array( 28 '#type' => 'textfield', 29 '#title' => t('Machine-readable name'), 30 '#description' => t('Example: image_gallery'). '<br/>' .t('May only contain lowercase letters, numbers and underscores. <strong>Try to avoid conflicts with the names of existing Drupal projects.</strong>'), 31 '#required' => TRUE, 32 '#default_value' => !empty($feature->name) ? $feature->name : '', 33 '#attributes' => array('class' => 'feature-module-name'), 34 '#element_validate' => array('features_export_form_validate_field'), 35 ); 36 // If recreating this feature, disable machine name field and blank out 37 // js-attachment classes to ensure the machine name cannot be changed. 38 if (isset($feature)) { 39 $form['info']['module_name']['#value'] = $feature->name; 40 $form['info']['module_name']['#disabled'] = TRUE; 41 $form['info']['name']['#attributes'] = array(); 42 } 43 $form['info']['description'] = array( 44 '#title' => t('Description'), 45 '#description' => t('Provide a short description of what users should expect when they enable your feature.'), 46 '#type' => 'textfield', 47 '#required' => TRUE, 48 '#default_value' => !empty($feature->info['description']) ? $feature->info['description'] : '', 49 ); 50 $form['info']['version'] = array( 51 '#title' => t('Version'), 52 '#description' => t('Examples: 6.x-1.0, 6.x-1.0-beta1'), 53 '#type' => 'textfield', 54 '#required' => FALSE, 55 '#default_value' => !empty($feature->info['version']) ? $feature->info['version'] : '', 56 '#size' => 30, 57 '#element_validate' => array('features_export_form_validate_field'), 58 ); 59 $form['info']['project_status_url'] = array( 60 '#title' => t('URL of update XML'), 61 '#description' => t('Example: http://mywebsite.com/fserver'), 62 '#type' => 'textfield', 63 '#required' => FALSE, 64 '#default_value' => !empty($feature->info['project status url']) ? $feature->info['project status url'] : '', 65 '#size' => 30, 66 '#element_validate' => array('features_export_form_validate_field'), 67 ); 68 69 // User-selected feature source components. 70 $components = features_get_components(); 71 ksort($components); 72 73 $form['export'] = array( 74 '#type' => 'fieldset', 75 '#tree' => FALSE, 76 '#theme' => 'features_form_export', 77 ); 78 $form['export']['components'] = array( 79 '#title' => t('Edit components'), 80 '#type' => 'select', 81 '#options' => array('------'), 82 '#attributes' => array('class' => 'features-select-components'), 83 ); 84 $form['export']['sources'] = array( 85 '#tree' => TRUE, 86 '#theme' => 'features_form_components', 87 ); 88 foreach ($components as $component => $component_info) { 89 $options = features_invoke($component, 'features_export_options'); 90 if ($component === 'dependencies') { 91 $default_value = !empty($feature->info['dependencies']) ? $feature->info['dependencies'] : array(); 92 } 93 else { 94 $default_value = !empty($feature->info['features'][$component]) ? $feature->info['features'][$component] : array(); 95 } 96 if ($options) { 97 // Find all default components that are not provided by this feature and 98 // strip them out of the possible options. 99 if ($map = features_get_default_map($component)) { 100 foreach ($map as $k => $v) { 101 if (isset($options[$k]) && (!isset($feature->name) || $v !== $feature->name)) { 102 unset($options[$k]); 103 } 104 } 105 } 106 // Ensure all options are stripped of potentially bad values. 107 foreach ($options as $k => $v) { 108 $options[$k] = check_plain($v); 109 } 110 $form['export']['components']['#options'][$component] = (isset($component_info['name']) ? $component_info['name'] : $component); 111 if (!empty($options)) { 112 $form['export']['sources'][$component] = array( 113 '#type' => 'checkboxes', 114 '#options' => features_dom_encode_options($options), 115 '#title' => $component, 116 '#default_value' => features_dom_encode_options($default_value, FALSE), 117 '#ahah' => array( 118 'path' => 'admin/build/features/export/populate', 119 'wrapper' => 'features-export-populated', 120 ), 121 ); 122 } 123 else { 124 $form['export']['sources'][$component] = array( 125 '#type' => 'item', 126 '#title' => $component, 127 '#value' => t('All components of this type are exported by other features or modules.'), 128 ); 129 } 130 } 131 } 132 $form['export']['features'] = array( 133 '#tree' => TRUE, 134 '#type' => 'markup', 135 '#prefix' => "<div id='features-export-populated'>", 136 '#suffix' => "</div>", 137 '#value' => !empty($feature->info) ? theme('features_components', $feature->info, $feature->info['features']) : "<div class='placeholder'></div>", 138 ); 139 140 $form['buttons'] = array('#theme' => 'features_form_buttons', '#tree' => FALSE); 141 $form['buttons']['submit'] = array( 142 '#type' => 'submit', 143 '#value' => t('Download feature'), 144 '#weight' => 10, 145 '#submit' => array('features_export_build_form_submit'), 146 ); 147 return $form; 148 } 149 150 /** 151 * Validation for project field. 152 */ 153 function features_export_form_validate_field($element, &$form_state) { 154 switch ($element['#name']) { 155 case 'module_name': 156 if (!preg_match('!^[a-z0-9_]+$!', $element['#value'])) { 157 form_error($element, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.')); 158 } 159 // If user is filling out the feature name for the first time and uses 160 // the name of an existing module throw an error. 161 else if (empty($element['#default_value']) && features_get_info('module', $element['#value'])) { 162 form_error($element, t('A module by the name @name already exists on your site. Please choose a different name.', array('@name' => $element['#value']))); 163 } 164 break; 165 case 'project_status_url': 166 if (!empty($element['#value']) && !valid_url($element['#value'])) { 167 form_error($element, t('The URL %url is invalid. Please enter a fully-qualified URL, such as http://www.example.com/feed.xml.', array('%url' => $element['#value']))); 168 } 169 break; 170 case 'version': 171 preg_match('/^(?P<core>\d+\.x)-(?P<major>\d+)\.(?P<patch>\d+)-?(?P<extra>\w+)?$/', $element['#value'], $matches); 172 if (!empty($element['#value']) && !isset($matches['core'], $matches['major'])) { 173 form_error($element, t('Please enter a valid version with core and major version number. Example: !example', array('!example' => '6.x-1.0'))); 174 }; 175 break; 176 } 177 } 178 179 /** 180 * Submit handler for features_export_form_build(). 181 */ 182 function features_export_build_form_submit($form, &$form_state) { 183 module_load_include('inc', 'features', 'features.export'); 184 features_include(); 185 186 // Assemble the combined component list 187 $stub = array(); 188 $components = array_keys(features_get_components()); 189 foreach ($components as $component) { 190 // User-selected components take precedence. 191 if (!empty($form_state['values']['sources'][$component])) { 192 $stub[$component] = features_dom_decode_options(array_filter($form_state['values']['sources'][$component])); 193 } 194 // Only fallback to an existing feature's values if there are no export options for the component. 195 else if (!empty($form['#feature']->info['features'][$component])) { 196 $stub[$component] = $form['#feature']->info['features'][$component]; 197 } 198 } 199 200 // Generate populated feature 201 $module_name = $form_state['values']['module_name']; 202 $export = features_populate($stub, $form_state['values']['sources']['dependencies'], $module_name); 203 204 // Directly copy the following attributes 205 $attr = array('name', 'description'); 206 foreach ($attr as $key) { 207 $export[$key] = isset($form_state['values'][$key]) ? $form_state['values'][$key] : NULL; 208 } 209 // If either update status-related keys are provided, add a project key 210 // corresponding to the module name. 211 if (!empty($form_state['values']['version']) || !empty($form_state['values']['project_status_url'])) { 212 $export['project'] = $form_state['values']['module_name']; 213 } 214 if (!empty($form_state['values']['version'])) { 215 $export['version'] = $form_state['values']['version']; 216 } 217 if (!empty($form_state['values']['project_status_url'])) { 218 $export['project status url'] = $form_state['values']['project_status_url']; 219 } 220 221 // Generate download 222 if ($files = features_export_render($export, $module_name, TRUE)) { 223 $filename = (!empty($export['version']) ? "{$module_name}-{$export['version']}" : $module_name) . '.tar'; 224 225 // Clear out output buffer to remove any garbage from tar output. 226 if (ob_get_level()) { 227 ob_end_clean(); 228 } 229 230 drupal_set_header('Content-type: application/x-tar'); 231 drupal_set_header('Content-Disposition: attachment; filename="'. $filename .'"'); 232 233 $tar = array(); 234 $filenames = array(); 235 foreach ($files as $extension => $file_contents) { 236 if (!in_array($extension, array('module', 'info'))) { 237 $extension .= '.inc'; 238 } 239 $filenames[] = "{$module_name}.$extension"; 240 print features_tar_create("{$module_name}/{$module_name}.$extension", $file_contents); 241 } 242 if (features_get_modules($module_name, TRUE)) { 243 $module_path = drupal_get_path('module', $module_name); 244 // file_scan_directory() can throw warnings when using PHP 5.3, messing 245 // up the output of our file stream. Suppress errors in this one case in 246 // order to produce valid output. 247 foreach (@file_scan_directory($module_path, '.*') as $file) { 248 $filename = substr($file->filename, strlen($module_path) + 1); 249 if (!in_array($filename, $filenames)) { 250 // Add this file. 251 $contents = file_get_contents($file->filename); 252 print features_tar_create("{$module_name}/{$filename}", $contents); 253 unset($contents); 254 } 255 } 256 } 257 print pack("a1024",""); 258 exit; 259 } 260 } 261 262 /** 263 * AHAH handler for features_export_form_build(). 264 */ 265 function features_export_build_form_populate() { 266 module_load_include('inc', 'features', 'features.export'); 267 features_include(); 268 269 $form_state = array(); 270 $submitted = $_POST; 271 272 if ($form = form_get_cache($submitted['form_build_id'], $form_state)) { 273 $stub = array(); 274 275 // Assemble the combined component list 276 $components = array_keys(features_get_components()); 277 foreach ($components as $component) { 278 // User-selected components take precedence. 279 if (!empty($submitted['sources'][$component])) { 280 // Validate and set the default value for each selected option. This 281 foreach ($submitted['sources'][$component] as $key => $value) { 282 if (isset($form['export']['sources'][$component]['#options'][$key])) { 283 $form['export']['sources'][$component]['#default_value'][$key] = $value; 284 } 285 } 286 $stub[$component] = features_dom_decode_options(array_filter($submitted['sources'][$component])); 287 } 288 // Only fallback to an existing feature's values if there are no export options for the component. 289 else if (!isset($form['export']['sources'][$component]) && !empty($form['#feature']->info['features'][$component])) { 290 $stub[$component] = $form['#feature']->info['features'][$component]; 291 } 292 } 293 294 // Assemble dependencies 295 $dependencies = isset($submitted['sources']['dependencies']) ? $submitted['sources']['dependencies'] : array(); 296 297 // Generate populated feature 298 $module_name = isset($form['#feature'], $form['#feature']->name) ? $form['#feature']->name : ''; 299 $export = features_populate($stub, $dependencies, $module_name); 300 301 // Render component display 302 $components_rendered = theme('features_components', $export, $stub); 303 $form['export']['features']['#value'] = $components_rendered; 304 305 // Re-cache form. This ensures that if the form fails to validate, selected 306 // values are preserved for the user. 307 form_set_cache($submitted['form_build_id'], $form, $form_state); 308 309 drupal_json(array('status' => TRUE, 'data' => $components_rendered . theme('status_messages'))); 310 exit; 311 } 312 drupal_json(array('status' => FALSE, 'data' => '')); 313 exit; 314 } 315 316 /** 317 * admin/build/features page callback. 318 */ 319 function features_admin_form($form_state) { 320 // Load export functions to use in comparison. 321 module_load_include('inc', 'features', 'features.export'); 322 323 // Clear & rebuild key caches 324 module_rebuild_cache(); 325 features_rebuild(); 326 327 features_get_info(NULL, NULL, TRUE); 328 $modules = features_get_modules(); 329 $features = features_get_features(); 330 $conflicts = features_get_conflicts(); 331 332 foreach ($modules as $key => $module) { 333 if ($module->status && !empty($module->info['dependencies'])) { 334 foreach ($module->info['dependencies'] as $dependent) { 335 if (isset($features[$dependent])) { 336 $features[$dependent]->dependents[$key] = $module->info['name']; 337 } 338 } 339 } 340 } 341 342 $form = array( 343 '#features' => $features, 344 '#theme' => 'features_form', 345 ); 346 347 // Generate features form. 348 foreach ($features as $name => $module) { 349 $package_title = !empty($module->info['package']) ? $module->info['package'] : t('Other'); 350 $package = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $package_title)); 351 352 // Set up package elements 353 if (!isset($form[$package])) { 354 $form[$package] = array( 355 '#tree' => FALSE, 356 '#title' => t($package_title), 357 '#theme' => 'features_form_package' 358 ); 359 $form[$package]['links'] = 360 $form[$package]['version'] = 361 $form[$package]['weight'] = 362 $form[$package]['status'] = 363 $form[$package]['action'] = array('#tree' => TRUE); 364 } 365 366 $disabled = FALSE; 367 $description = isset($module->info['description']) ? $module->info['description'] : ''; 368 369 // Detect unmet dependencies 370 if (!empty($module->info['dependencies'])) { 371 $unmet_dependencies = array(); 372 $dependencies = _features_export_maximize_dependencies($module->info['dependencies']); 373 foreach ($dependencies as $dependency) { 374 if (empty($modules[$dependency])) { 375 $unmet_dependencies[] = theme('features_module_status', FEATURES_MODULE_MISSING, $dependency); 376 } 377 } 378 if (!empty($unmet_dependencies)) { 379 $description .= "<div class='dependencies'>". t('Unmet dependencies: !dependencies', array('!dependencies' => implode(', ', $unmet_dependencies))) ."</div>"; 380 $disabled = TRUE; 381 } 382 } 383 384 if (!empty($module->dependents)) { 385 $disabled = TRUE; 386 $description .= "<div class='requirements'>". t('Required by: !dependents', array('!dependents' => implode(', ', $module->dependents))) ."</div>"; 387 } 388 389 // Detect potential conflicts 390 if (!empty($conflicts[$name])) { 391 $module_conflicts = array(); 392 foreach (array_keys($conflicts[$name]) as $conflict) { 393 $module_conflicts[] = theme('features_module_status', FEATURES_MODULE_MISSING, $conflict); 394 // Only disable modules with conflicts if they are not already enabled. 395 // If they are already enabled, somehow the user got themselves into a 396 // bad situation and they need to be able to disable a conflicted module. 397 if (module_exists($conflict) && !module_exists($name)) { 398 $disabled = TRUE; 399 } 400 } 401 $description .= "<div class='conflicts'>". t('Conflicts with: !conflicts', array('!conflicts' => implode(', ', $module_conflicts))) ."</div>"; 402 } 403 404 $form[$package]['status'][$name] = array( 405 '#type' => 'checkbox', 406 '#title' => $module->info['name'], 407 '#description' => $description, 408 '#default_value' => $module->status, 409 '#disabled' => $disabled, 410 ); 411 412 if (!empty($module->info['project status url'])) { 413 $uri = l(truncate_utf8($module->info['project status url'], 35, TRUE, TRUE), $module->info['project status url']); 414 } 415 else if (isset($module->info['project'], $module->info['version'], $module->info['datestamp'])) { 416 $uri = l('http://drupal.org', 'http://drupal.org/project/'. $module->info['project']); 417 } 418 else { 419 $uri = t('Unavailable'); 420 } 421 $version = !empty($module->info['version']) ? $module->info['version'] : ''; 422 $version = !empty($version) ? "<div class='description'>$version</div>" : ''; 423 $form[$package]['sign'][$name] = array('#type' => 'markup', '#value' => "{$uri} {$version}"); 424 425 if (user_access('administer features')) { 426 // Add status link 427 $href = "admin/build/features/{$name}"; 428 if ($module->status) { 429 $state = '<span class="admin-loading features-storage">' . t('Checking...') . '</span>'; 430 $state .= l(t('Check'), "admin/build/features/{$name}/status", array('attributes' => array('class' => 'admin-check'))); 431 $state .= theme('features_storage_link', FEATURES_REBUILDING, NULL, $href); 432 $state .= theme('features_storage_link', FEATURES_NEEDS_REVIEW, NULL, $href); 433 $state .= theme('features_storage_link', FEATURES_OVERRIDDEN, NULL, $href); 434 $state .= theme('features_storage_link', FEATURES_DEFAULT, NULL, $href); 435 } 436 elseif (!empty($conflicts[$name])) { 437 $state = theme('features_storage_link', FEATURES_CONFLICT, NULL, $href); 438 } 439 else { 440 $state = theme('features_storage_link', FEATURES_DISABLED, NULL, $href); 441 } 442 $form[$package]['state'][$name] = array( 443 '#type' => 'markup', 444 '#value' => !empty($state) ? $state : '', 445 ); 446 447 // Add in recreate link 448 $form[$package]['actions'][$name] = array( 449 '#type' => 'markup', 450 '#value' => l(t('Recreate'), "admin/build/features/{$name}/recreate", array('attributes' => array('class' => 'admin-update'))), 451 ); 452 } 453 } 454 455 $form['buttons'] = array( 456 '#theme' => 'features_form_buttons', 457 ); 458 $form['buttons']['submit'] = array( 459 '#type' => 'submit', 460 '#value' => t('Save settings'), 461 '#submit' => array('features_form_submit'), 462 '#validate' => array('features_form_validate'), 463 ); 464 return $form; 465 } 466 467 /** 468 * Display the components of a feature. 469 */ 470 function features_admin_components($form_state, $feature) { 471 module_load_include('inc', 'features', 'features.export'); 472 $form = array(); 473 474 // Store feature info for theme layer. 475 $form['module'] = array('#type' => 'value', '#value' => $feature->name); 476 $form['#info'] = $feature->info; 477 $form['#dependencies'] = array(); 478 if (!empty($feature->info['dependencies'])) { 479 foreach ($feature->info['dependencies'] as $dependency) { 480 $status = features_get_module_status($dependency); 481 $form['#dependencies'][$dependency] = $status; 482 } 483 } 484 485 $review = $revert = FALSE; 486 487 // Iterate over components and retrieve status for display 488 $states = features_get_component_states(array($feature->name), FALSE); 489 $form['revert']['#tree'] = TRUE; 490 foreach ($feature->info['features'] as $component => $items) { 491 if (user_access('administer features') && in_array($states[$feature->name][$component], array(FEATURES_OVERRIDDEN, FEATURES_NEEDS_REVIEW))) { 492 switch ($states[$feature->name][$component]) { 493 case FEATURES_OVERRIDDEN: 494 $revert = TRUE; 495 break; 496 case FEATURES_NEEDS_REVIEW: 497 $review = TRUE; 498 break; 499 } 500 $form['revert'][$component] = array( 501 '#type' => 'checkbox', 502 '#default_value' => FALSE, 503 ); 504 } 505 if (module_exists('diff')) { 506 $item = menu_get_item("admin/build/features/{$feature->name}/diff/{$component}"); 507 $path = ($item && $item['access']) ? $item['href'] : NULL; 508 } 509 else { 510 $path = NULL; 511 } 512 $form['components'][$component] = array( 513 '#type' => 'markup', 514 '#value' => theme('features_storage_link', $states[$feature->name][$component], NULL, $path), 515 ); 516 } 517 518 if ($review || $revert) { 519 $form['buttons'] = array('#theme' => 'features_form_buttons', '#tree' => TRUE); 520 if ($revert || $review) { 521 $form['buttons']['revert'] = array( 522 '#type' => 'submit', 523 '#value' => t('Revert components'), 524 '#submit' => array('features_admin_components_revert'), 525 ); 526 } 527 if ($review) { 528 $form['buttons']['review'] = array( 529 '#type' => 'submit', 530 '#value' => t('Mark as reviewed'), 531 '#submit' => array('features_admin_components_review'), 532 ); 533 } 534 } 535 return $form; 536 } 537 538 /** 539 * Submit handler for revert form. 540 */ 541 function features_admin_components_revert(&$form, &$form_state) { 542 module_load_include('inc', 'features', 'features.export'); 543 features_include(); 544 $module = $form_state['values']['module']; 545 $revert = array(); 546 foreach (array_filter($form_state['values']['revert']) as $component => $status) { 547 $revert[$module][] = $component; 548 drupal_set_message(t('Reverted all <strong>!component</strong> components for <strong>!module</strong>.', array('!component' => $component, '!module' => $module))); 549 } 550 features_revert($revert); 551 $form_state['redirect'] = 'admin/build/features/'. $module; 552 } 553 554 /** 555 * Submit handler for revert form. 556 */ 557 function features_admin_components_review(&$form, &$form_state) { 558 module_load_include('inc', 'features', 'features.export'); 559 features_include(); 560 $module = $form_state['values']['module']; 561 $revert = array(); 562 foreach (array_filter($form_state['values']['revert']) as $component => $status) { 563 features_set_signature($module, $component); 564 drupal_set_message(t('All <strong>!component</strong> components for <strong>!module</strong> reviewed.', array('!component' => $component, '!module' => $module))); 565 } 566 $form_state['redirect'] = 'admin/build/features/'. $module; 567 } 568 569 /** 570 * Validate handler for the 'manage features' form. 571 */ 572 function features_form_validate(&$form, &$form_state) { 573 include_once './includes/install.inc'; 574 $conflicts = features_get_conflicts(); 575 foreach ($form_state['values']['status'] as $module => $status) { 576 if ($status) { 577 if (!empty($conflicts[$module])) { 578 foreach (array_keys($conflicts[$module]) as $conflict) { 579 if (!empty($form_state['values']['status'][$conflict])) { 580 form_set_error('status', t('The feature !module cannot be enabled because it conflicts with !conflict.', array('!module' => $module, '!conflict' => $conflict))); 581 } 582 } 583 } 584 if (!drupal_check_module($module)) { 585 form_set_error('status', t('The feature !module cannot be enabled because it has unmet requirements.', array('!module' => $module, '!conflict' => $conflict))); 586 } 587 } 588 } 589 } 590 591 /** 592 * Submit handler for the 'manage features' form 593 */ 594 function features_form_submit(&$form, &$form_state) { 595 // Clear drupal caches after enabling a feature. We do this in a separate 596 // page callback rather than as part of the submit handler as some modules 597 // have includes/other directives of importance in hooks that have already 598 // been called in this page load. 599 $form_state['redirect'] = 'admin/build/features/cleanup/clear'; 600 601 $features = $form['#features']; 602 if (!empty($features)) { 603 $status = $form_state['values']['status']; 604 $install = array_keys(array_filter($status)); 605 $disable = array_diff(array_keys($status), $install); 606 607 // Disable first. If there are any features that are disabled that are 608 // dependencies of features that have been queued for install, they will 609 // be re-enabled. 610 module_disable($disable); 611 features_install_modules($install); 612 } 613 } 614 615 /** 616 * Form for disabling orphaned dependencies. 617 */ 618 function features_cleanup_form($form_state, $cache_clear = FALSE) { 619 $form = array(); 620 621 // Clear caches if we're getting a post-submit redirect that requests it. 622 if ($cache_clear) { 623 drupal_flush_all_caches(); 624 625 // The following functions need to be run because drupal_flush_all_caches() 626 // runs rebuilds in the wrong order. The node type cache is rebuilt *after* 627 // the menu is rebuilt, meaning that the menu tree is stale in certain 628 // circumstances after drupal_flush_all_caches(). We rebuild again. 629 menu_rebuild(); 630 } 631 632 // Retrieve orphaned modules and provide them as optional modules to be disabled. 633 // Exclude any modules that have been added to the 'ignored' list. 634 $options = array(); 635 $orphans = features_get_orphans(); 636 $ignored = variable_get('features_ignored_orphans', array()); 637 if (!empty($orphans)) { 638 foreach ($orphans as $module) { 639 if (!in_array($module->name, $ignored, TRUE)) { 640 $options[$module->name] = check_plain($module->info['name']); 641 } 642 } 643 } 644 645 if (!empty($options)) { 646 $form['orphans'] = array( 647 '#title' => t('Orphaned dependencies'), 648 '#description' => t('These modules are dependencies of features that have been disabled. They may be disabled without affecting other components of your website.'), 649 '#type' => 'checkboxes', 650 '#options' => $options, 651 '#default_value' => array_keys($options), 652 ); 653 $form['buttons'] = array('#tree' => TRUE, '#theme' => 'features_form_buttons'); 654 $form['buttons']['disable'] = array( 655 '#type' => 'submit', 656 '#value' => t('Disable selected modules'), 657 '#submit' => array('features_cleanup_form_disable'), 658 ); 659 $form['buttons']['ignore'] = array( 660 '#type' => 'submit', 661 '#value' => t('Leave enabled'), 662 '#submit' => array('features_cleanup_form_ignore'), 663 ); 664 } 665 else { 666 drupal_goto('admin/build/features'); 667 } 668 return $form; 669 } 670 671 /** 672 * Submit handler for disable action on features_cleanup_form(). 673 */ 674 function features_cleanup_form_disable(&$form, &$form_state) { 675 if (!empty($form_state['values']['orphans'])) { 676 $disable = array_keys(array_filter($form_state['values']['orphans'])); 677 $ignored = array_diff(array_keys($form_state['values']['orphans']), $disable); 678 679 // Disable any orphans that have been selected. 680 module_disable($disable); 681 drupal_flush_all_caches(); 682 683 // Add enabled modules to ignored orphans list. 684 $ignored_orphans = variable_get('features_ignored_orphans', array()); 685 foreach ($ignored as $module) { 686 $ignored_orphans[$module] = $module; 687 } 688 variable_set('features_ignored_orphans', $ignored_orphans); 689 } 690 $form_state['redirect'] = 'admin/build/features/cleanup'; 691 } 692 693 /** 694 * Submit handler for ignore action on features_cleanup_form(). 695 */ 696 function features_cleanup_form_ignore(&$form, &$form_state) { 697 if (!empty($form_state['values']['orphans'])) { 698 $ignored = array_keys($form_state['values']['orphans']); 699 $ignored_orphans = variable_get('features_ignored_orphans', array()); 700 foreach ($ignored as $module) { 701 $ignored_orphans[$module] = $module; 702 } 703 variable_set('features_ignored_orphans', $ignored_orphans); 704 } 705 $form_state['redirect'] = 'admin/build/features/cleanup'; 706 } 707 708 /** 709 * Page callback to display the differences between what's in code and 710 * what is in the db. 711 * 712 * @param $feature 713 * A loaded feature object to display differences for. 714 * @param $component 715 * Optional: specific component to display differences for. If excluded, all components are used. 716 * 717 * @return Themed display of what is different. 718 */ 719 function features_feature_diff($feature, $component = NULL) { 720 drupal_add_css(drupal_get_path('module', 'features') .'/features.css'); 721 module_load_include('inc', 'features', 'features.export'); 722 723 $overrides = features_detect_overrides($feature); 724 725 $output = ''; 726 if (!empty($overrides)) { 727 // Filter overrides down to specified component. 728 if (isset($component) && isset($overrides[$component])) { 729 $overrides = array($component => $overrides[$component]); 730 } 731 732 module_load_include('php', 'diff', 'DiffEngine'); 733 $formatter = new DrupalDiffFormatter(); 734 $rows = array(); 735 foreach ($overrides as $component => $items) { 736 $diff = new Diff(explode("\n", $items['default']), explode("\n", $items['normal'])); 737 $rows[] = array(array('data' => $component, 'colspan' => 4, 'header' => TRUE)); 738 $rows = array_merge($rows, $formatter->format($diff)); 739 } 740 $header = array( 741 array('data' => t('Default'), 'colspan' => 2), 742 array('data' => t('Overrides'), 'colspan' => 2), 743 ); 744 $output .= theme('diff_table', $header, $rows, array('class' => 'diff features-diff')); 745 } 746 else { 747 $output = "<div class='features-empty'>". t('No changes have been made to this feature.') ."</div>"; 748 } 749 $output = "<div class='features-comparison'>{$output}</div>"; 750 return $output; 751 } 752 753 /** 754 * Javascript call back that returns the status of a feature. 755 */ 756 function features_feature_status($feature) { 757 module_load_include('inc', 'features', 'features.export'); 758 return drupal_json(array('storage' => features_get_storage($feature->name))); 759 } 760 761 /** 762 * Make a Drupal options array safe for usage with jQuery DOM selectors. 763 * Encodes known bad characters into __[ordinal]__ so that they may be 764 * safely referenced by JS behaviors. 765 */ 766 function features_dom_encode_options($options = array(), $keys_only = TRUE) { 767 $replacements = array( 768 ':' => '__'. ord(':') .'__', 769 '/' => '__'. ord('/') .'__', 770 ',' => '__'. ord(',') .'__', 771 '.' => '__'. ord(',') .'__', 772 '<' => '__'. ord('<') .'__', 773 '>' => '__'. ord('>') .'__', 774 ); 775 $encoded = array(); 776 foreach ($options as $key => $value) { 777 $encoded[strtr($key, $replacements)] = $keys_only ? $value : strtr($value, $replacements); 778 } 779 return $encoded; 780 } 781 782 /** 783 * Decode an array of option values that have been encoded by 784 * features_dom_encode_options(). 785 */ 786 function features_dom_decode_options($options, $keys_only = FALSE) { 787 $replacements = array_flip(array( 788 ':' => '__'. ord(':') .'__', 789 '/' => '__'. ord('/') .'__', 790 ',' => '__'. ord(',') .'__', 791 '.' => '__'. ord(',') .'__', 792 '<' => '__'. ord('<') .'__', 793 '>' => '__'. ord('>') .'__', 794 )); 795 $encoded = array(); 796 foreach ($options as $key => $value) { 797 $encoded[strtr($key, $replacements)] = $keys_only ? $value : strtr($value, $replacements); 798 } 799 return $encoded; 800 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |