You are here

public static function Features::revert in Hook Update Deploy Tools 8

Same name and namespace in other branches
  1. 7 src/Features.php \HookUpdateDeployTools\Features::revert()

Safely revert an array of Features and provide feedback.

The safety steps include: a) Making sure the Feature exists (is enabled). b) Checks to see if the Feature is overridden.

Parameters

string[]|string $feature_names: One or more features or feature.component pairs. (in order)

bool $force: Force revert even if Features assumes components' state are default.

Return value

string Messsage indicating progress of feature reversions.

Throws

\DrupalUpdateException Calls the update a failure, preventing it from registering the update_N.

File

src/Features.php, line 27

Class

Features
Public method for reverting Features only if needed.

Namespace

HookUpdateDeployTools

Code

public static function revert($feature_names, $force = FALSE) {
  $feature_names = (array) $feature_names;
  $completed = array();
  $message = '';
  $total_requested = count($feature_names);
  $t = get_t();
  try {
    Check::canUse('features');
    module_load_include('inc', 'features', 'features.export');

    // Check all functions that we plan to call are available.
    // Exceptions are preferable to fatal errors.
    Check::canCall('features_include');
    Check::canCall('features_load_feature');
    Check::canCall('features_hook');

    // Pick up new files that may have been added to existing Features.
    features_include(TRUE);
    $feature_names = self::parseFeatureNames($feature_names);

    // See if the feature needs to be reverted.
    foreach ($feature_names as $feature_name => $components_needed) {
      self::hasComponent($feature_name, $components_needed);
      $variables = array(
        '@feature_name' => $feature_name,
      );

      // If the Feature exists, process it.
      if (Check::canUse($feature_name) && ($feature = features_load_feature($feature_name, TRUE))) {
        $components = array();
        if ($force) {

          // Forcefully revert all components of the Feature.
          // Gather the components to revert them all.
          foreach (array_keys($feature->info['features']) as $component) {
            if (features_hook($component, 'features_revert')) {
              $components[] = $component;
            }
          }
          $message = "Revert (forced): @feature_name.";
          Message::make($message, $variables, WATCHDOG_INFO, 2);
        }
        else {

          // Only revert components that are detected to be
          // Overridden || Needs review || Rebuildable.
          $message = "Revert: @feature_name.";
          $states = features_get_component_states(array(
            $feature->name,
          ), FALSE);

          // Build list of components that can be reverted and need to be.
          foreach ($states[$feature->name] as $component => $state) {
            $revertable_states = array(
              FEATURES_OVERRIDDEN,
              FEATURES_NEEDS_REVIEW,
              FEATURES_REBUILDABLE,
            );

            // If they are revertable, add them to the list.
            if (in_array($state, $revertable_states) && features_hook($component, 'features_revert')) {
              $components[] = $component;
            }
          }
        }
        if (!empty($components_needed) && is_array($components_needed)) {

          // From list of components that need to be reverted, keep only the
          // components that were requested.
          $components = array_intersect($components, $components_needed);
        }
        if (empty($components)) {

          // Not overridden, no revert required.
          $message = 'Skipped - not currently overridden.';
          $completed[$feature_name] = $message;
        }

        // Process the revert on each component.
        foreach ($components as $component) {
          $variables['@component'] = $component;
          if (features_feature_is_locked($feature_name, $component)) {

            // Trying to revert a locked component should raise an exception,
            // but it may have been caused by a blanket revert, so just raise
            // a warning instead of failing the update.
            $message = 'Revert @feature_name.@component: Skipped - locked.';
            Message::make($message, $variables, WATCHDOG_WARNING);
            $completed[$feature_name][$component] = 'Skipped - locked.';
          }
          else {

            // Ok to proceed by reverting this component.
            features_revert(array(
              $feature_name => array(
                $component,
              ),
            ));

            // Now check to see if it actually reverted.
            if (self::isOverridden($feature_name, $component)) {
              $message = '@feature_name.@component: Remains overridden. Check for issues.';
              global $base_url;
              $link = $base_url . '/admin/structure/features';
              Message::make($message, $variables, WATCHDOG_WARNING, 1, $link);
              $message = "Overridden - Check for issues.";
            }
            else {

              // Not shown as overridden so most likely a success.
              $message = "Success";
            }
            $completed[$feature_name][$component] = format_string($message, $variables);
          }
        }
        $variables['!completed'] = $completed[$feature_name];

        // Log a message about the entire Feature.
        $message = "Reverted @feature_name : !completed";
        Message::make($message, $variables, WATCHDOG_INFO);
      }
    }
  } catch (\Exception $e) {
    $vars = array(
      '!error' => method_exists($e, 'logMessage') ? $e
        ->logMessage() : $e
        ->getMessage(),
    );
    if (!method_exists($e, 'logMessage')) {

      // Not logged yet, so log it.
      $message = 'Feature revert denied because: !error';
      Message::make($message, $vars, WATCHDOG_ERROR);
    }

    // Output a summary before shutting this down.
    $done = HudtInternal::getSummary($completed, $total_requested, 'Reverted');
    Message::make($done, array(), FALSE, 1);
    throw new HudtException('Caught Exception: Update aborted!  !error', $vars, WATCHDOG_ERROR, FALSE);
  }

  // Log and output a summary of all the requests.
  $done = HudtInternal::getSummary($completed, $total_requested, 'Reverted');
  $message = Message::make('The requested reverts were processed. !done', array(
    '!done' => $done,
  ), WATCHDOG_INFO);
  return $message;
}