Version 1
[yaffs-website] / vendor / drush / drush / lib / Drush / UpdateService / StatusInfoDrush.php
diff --git a/vendor/drush/drush/lib/Drush/UpdateService/StatusInfoDrush.php b/vendor/drush/drush/lib/Drush/UpdateService/StatusInfoDrush.php
new file mode 100644 (file)
index 0000000..636cae4
--- /dev/null
@@ -0,0 +1,420 @@
+<?php
+
+/**
+ * @file
+ * Implementation of 'drush' update_status engine for any Drupal version.
+ */
+
+namespace Drush\UpdateService;
+
+use Drush\Log\LogLevel;
+
+class StatusInfoDrush implements StatusInfoInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct($type, $engine, $config) {
+    $this->engine_type = $type;
+    $this->engine = $engine;
+    $this->engine_config = $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function lastCheck() {
+    $older = 0;
+
+    // Iterate all projects and get the time of the older release info.
+    $projects = drush_get_projects();
+    foreach ($projects as $project_name => $project) {
+      $request = pm_parse_request($project_name, NULL, $projects);
+      $url = Project::buildFetchUrl($request);
+      $cache_file = drush_download_file_name($url);
+      if (file_exists($cache_file)) {
+        $ctime = filectime($cache_file);
+        $older = (!$older) ? $ctime : min($ctime, $older);
+      }
+    }
+
+    return $older;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function refresh() {
+    $release_info = drush_include_engine('release_info', 'updatexml');
+
+    // Clear all caches for the available projects.
+    $projects = drush_get_projects();
+    foreach ($projects as $project_name => $project) {
+      $request = pm_parse_request($project_name, NULL, $projects);
+      $release_info->clearCached($request);
+    }
+  }
+
+  /**
+   * Get update information for all installed projects.
+   *
+   * @return
+   *   Array of update status information.
+   */
+  function getStatus($projects, $check_disabled) {
+    // Exclude disabled projects.
+    if (!$check_disabled) {
+      foreach ($projects as $project_name => $project) {
+        if (!$project['status']) {
+          unset($projects[$project_name]);
+        }
+      }
+    }
+    $available = $this->getAvailableReleases($projects);
+    $update_info = $this->calculateUpdateStatus($available, $projects);
+    return $update_info;
+  }
+
+  /**
+   * Obtains release info for projects.
+   */
+  private function getAvailableReleases($projects) {
+    drush_log(dt('Checking available update data ...'), LogLevel::OK);
+
+    $release_info = drush_include_engine('release_info', 'updatexml');
+
+    $available = array();
+    foreach ($projects as $project_name => $project) {
+      // Discard projects with unknown installation path.
+      if ($project_name != 'drupal' && !isset($project['path'])) {
+        continue;
+      }
+      drush_log(dt('Checking available update data for !project.', array('!project' => $project['label'])), LogLevel::OK);
+      $request = $project_name . (isset($project['core']) ? '-' . $project['core'] : '');
+      $request = pm_parse_request($request, NULL, $projects);
+      $project_release_info = $release_info->get($request);
+      if ($project_release_info) {
+        $available[$project_name] = $project_release_info;
+      }
+    }
+
+    // Clear any error set by a failed project. This avoid rollbacks.
+    drush_clear_error();
+
+    return $available;
+  }
+
+  /**
+   * Calculates update status for given projects.
+   */
+  private function calculateUpdateStatus($available, $projects) {
+    $update_info = array();
+    foreach ($available as $project_name => $project_release_info) {
+      // Obtain project 'global' status. NULL status is ok (project published),
+      // otherwise it signals something is bad with the project (revoked, etc).
+      $project_status = $this->calculateProjectStatus($project_release_info);
+      // Discard custom projects.
+      if ($project_status == DRUSH_UPDATESTATUS_UNKNOWN) {
+        continue;
+      }
+
+      // Prepare update info.
+      $project = $projects[$project_name];
+      $is_core = ($project['type'] == 'core');
+      $version = pm_parse_version($project['version'], $is_core);
+      // If project version ends with 'dev', this is a dev snapshot.
+      $install_type = (substr($project['version'], -3, 3) == 'dev') ? 'dev' : 'official';
+      $project_update_info = array(
+        'name'             => $project_name,
+        'label'            => $project['label'],
+        'path'             => isset($project['path']) ? $project['path'] : '',
+        'install_type'     => $install_type,
+        'existing_version' => $project['version'],
+        'existing_major'   => $version['version_major'],
+        'status'           => $project_status,
+        'datestamp'        => empty($project['datestamp']) ? NULL : $project['datestamp'],
+      );
+
+      // If we don't have a project status yet, it means this is
+      // a published project and we need to obtain its update status
+      // and recommended release.
+      if (is_null($project_status)) {
+        $this->calculateProjectUpdateStatus($project_release_info, $project_update_info);
+      }
+
+      // We want to ship all release info data including all releases,
+      // not just the ones selected by calculateProjectUpdateStatus().
+      // We use it to allow the user to update to a specific version.
+      unset($project_update_info['releases']);
+      $update_info[$project_name] = $project_update_info + $project_release_info->getInfo();
+    }
+
+    return $update_info;
+  }
+
+  /**
+   * Obtain the project status in the update service.
+   *
+   * This is not the update status of the installed version
+   * but the project 'global' status (unpublished, revoked, etc).
+   *
+   * @see update_calculate_project_status().
+   */
+  private function calculateProjectStatus($project_release_info) {
+    $project_status = NULL;
+
+    // If connection to the update service went wrong, or the received xml
+    // is malformed, we don't have a UpdateService::Project object.
+    if (!$project_release_info) {
+      $project_status = DRUSH_UPDATESTATUS_NOT_FETCHED;
+    }
+    else {
+      switch ($project_release_info->getStatus()) {
+        case 'insecure':
+          $project_status = DRUSH_UPDATESTATUS_NOT_SECURE;
+          break;
+        case 'unpublished':
+        case 'revoked':
+          $project_status = DRUSH_UPDATESTATUS_REVOKED;
+          break;
+        case 'unsupported':
+          $project_status = DRUSH_UPDATESTATUS_NOT_SUPPORTED;
+          break;
+        case 'unknown':
+          $project_status = DRUSH_UPDATESTATUS_UNKNOWN;
+          break;
+      }
+    }
+    return $project_status;
+  }
+
+  /**
+   * Obtain the update status of a project and the recommended release.
+   *
+   * This is a stripped down version of update_calculate_project_status().
+   * That function has the same logic in Drupal 6,7,8.
+   * Note: in Drupal 6 this is part of update_calculate_project_data().
+   *
+   * @see update_calculate_project_status().
+   */
+  private function calculateProjectUpdateStatus($project_release_info, &$project_data) {
+    $available = $project_release_info->getInfo();
+
+    /**
+     * Here starts the code adapted from update_calculate_project_status().
+     * Line 492 in Drupal 7.
+     *
+     * Changes are:
+     *   - Use DRUSH_UPDATESTATUS_* constants instead of DRUSH_UPDATESTATUS_*
+     *   - Remove error conditions we already handle
+     *   - Remove presentation code ('extra' and 'reason' keys in $project_data)
+     *   - Remove "also available" information.
+     */
+
+    // Figure out the target major version.
+    $existing_major = $project_data['existing_major'];
+    $supported_majors = array();
+    if (isset($available['supported_majors'])) {
+      $supported_majors = explode(',', $available['supported_majors']);
+    }
+    elseif (isset($available['default_major'])) {
+      // Older release history XML file without supported or recommended.
+      $supported_majors[] = $available['default_major'];
+    }
+
+    if (in_array($existing_major, $supported_majors)) {
+      // Still supported, stay at the current major version.
+      $target_major = $existing_major;
+    }
+    elseif (isset($available['recommended_major'])) {
+      // Since 'recommended_major' is defined, we know this is the new XML
+      // format. Therefore, we know the current release is unsupported since
+      // its major version was not in the 'supported_majors' list. We should
+      // find the best release from the recommended major version.
+      $target_major = $available['recommended_major'];
+      $project_data['status'] = DRUSH_UPDATESTATUS_NOT_SUPPORTED;
+    }
+    elseif (isset($available['default_major'])) {
+      // Older release history XML file without recommended, so recommend
+      // the currently defined "default_major" version.
+      $target_major = $available['default_major'];
+    }
+    else {
+      // Malformed XML file? Stick with the current version.
+      $target_major = $existing_major;
+    }
+
+    // Make sure we never tell the admin to downgrade. If we recommended an
+    // earlier version than the one they're running, they'd face an
+    // impossible data migration problem, since Drupal never supports a DB
+    // downgrade path. In the unfortunate case that what they're running is
+    // unsupported, and there's nothing newer for them to upgrade to, we
+    // can't print out a "Recommended version", but just have to tell them
+    // what they have is unsupported and let them figure it out.
+    $target_major = max($existing_major, $target_major);
+
+    $release_patch_changed = '';
+    $patch = '';
+
+    foreach ($available['releases'] as $version => $release) {
+      // First, if this is the existing release, check a few conditions.
+      if ($project_data['existing_version'] === $version) {
+        if (isset($release['terms']['Release type']) &&
+            in_array('Insecure', $release['terms']['Release type'])) {
+          $project_data['status'] = DRUSH_UPDATESTATUS_NOT_SECURE;
+        }
+        elseif ($release['status'] == 'unpublished') {
+          $project_data['status'] = DRUSH_UPDATESTATUS_REVOKED;
+        }
+        elseif (isset($release['terms']['Release type']) &&
+                in_array('Unsupported', $release['terms']['Release type'])) {
+          $project_data['status'] = DRUSH_UPDATESTATUS_NOT_SUPPORTED;
+        }
+      }
+
+      // Otherwise, ignore unpublished, insecure, or unsupported releases.
+      if ($release['status'] == 'unpublished' ||
+          (isset($release['terms']['Release type']) &&
+           (in_array('Insecure', $release['terms']['Release type']) ||
+            in_array('Unsupported', $release['terms']['Release type'])))) {
+        continue;
+      }
+
+      // See if this is a higher major version than our target and discard it.
+      // Note: at this point Drupal record it as an "Also available" release.
+      if (isset($release['version_major']) && $release['version_major'] > $target_major) {
+        continue;
+      }
+
+      // Look for the 'latest version' if we haven't found it yet. Latest is
+      // defined as the most recent version for the target major version.
+      if (!isset($project_data['latest_version'])
+          && $release['version_major'] == $target_major) {
+        $project_data['latest_version'] = $version;
+        $project_data['releases'][$version] = $release;
+      }
+
+      // Look for the development snapshot release for this branch.
+      if (!isset($project_data['dev_version'])
+          && $release['version_major'] == $target_major
+          && isset($release['version_extra'])
+          && $release['version_extra'] == 'dev') {
+        $project_data['dev_version'] = $version;
+        $project_data['releases'][$version] = $release;
+      }
+
+      // Look for the 'recommended' version if we haven't found it yet (see
+      // phpdoc at the top of this function for the definition).
+      if (!isset($project_data['recommended'])
+          && $release['version_major'] == $target_major
+          && isset($release['version_patch'])) {
+        if ($patch != $release['version_patch']) {
+          $patch = $release['version_patch'];
+          $release_patch_changed = $release;
+        }
+        if (empty($release['version_extra']) && $patch == $release['version_patch']) {
+          $project_data['recommended'] = $release_patch_changed['version'];
+          $project_data['releases'][$release_patch_changed['version']] = $release_patch_changed;
+        }
+      }
+
+      // Stop searching once we hit the currently installed version.
+      if ($project_data['existing_version'] === $version) {
+        break;
+      }
+
+      // If we're running a dev snapshot and have a timestamp, stop
+      // searching for security updates once we hit an official release
+      // older than what we've got. Allow 100 seconds of leeway to handle
+      // differences between the datestamp in the .info file and the
+      // timestamp of the tarball itself (which are usually off by 1 or 2
+      // seconds) so that we don't flag that as a new release.
+      if ($project_data['install_type'] == 'dev') {
+        if (empty($project_data['datestamp'])) {
+          // We don't have current timestamp info, so we can't know.
+          continue;
+        }
+        elseif (isset($release['date']) && ($project_data['datestamp'] + 100 > $release['date'])) {
+          // We're newer than this, so we can skip it.
+          continue;
+        }
+      }
+
+      // See if this release is a security update.
+      if (isset($release['terms']['Release type'])
+          && in_array('Security update', $release['terms']['Release type'])) {
+        $project_data['security updates'][] = $release;
+      }
+    }
+
+    // If we were unable to find a recommended version, then make the latest
+    // version the recommended version if possible.
+    if (!isset($project_data['recommended']) && isset($project_data['latest_version'])) {
+      $project_data['recommended'] = $project_data['latest_version'];
+    }
+
+    //
+    // Check to see if we need an update or not.
+    //
+
+    if (!empty($project_data['security updates'])) {
+      // If we found security updates, that always trumps any other status.
+      $project_data['status'] = DRUSH_UPDATESTATUS_NOT_SECURE;
+    }
+
+    if (isset($project_data['status'])) {
+      // If we already know the status, we're done.
+      return;
+    }
+
+    // If we don't know what to recommend, there's nothing we can report.
+    // Bail out early.
+    if (!isset($project_data['recommended'])) {
+      $project_data['status'] = DRUSH_UPDATESTATUS_UNKNOWN;
+      $project_data['reason'] = t('No available releases found');
+      return;
+    }
+
+    // If we're running a dev snapshot, compare the date of the dev snapshot
+    // with the latest official version, and record the absolute latest in
+    // 'latest_dev' so we can correctly decide if there's a newer release
+    // than our current snapshot.
+    if ($project_data['install_type'] == 'dev') {
+      if (isset($project_data['dev_version']) && $available['releases'][$project_data['dev_version']]['date'] > $available['releases'][$project_data['latest_version']]['date']) {
+        $project_data['latest_dev'] = $project_data['dev_version'];
+      }
+      else {
+        $project_data['latest_dev'] = $project_data['latest_version'];
+      }
+    }
+
+    // Figure out the status, based on what we've seen and the install type.
+    switch ($project_data['install_type']) {
+      case 'official':
+        if ($project_data['existing_version'] === $project_data['recommended'] || $project_data['existing_version'] === $project_data['latest_version']) {
+          $project_data['status'] = DRUSH_UPDATESTATUS_CURRENT;
+        }
+        else {
+          $project_data['status'] = DRUSH_UPDATESTATUS_NOT_CURRENT;
+        }
+        break;
+
+      case 'dev':
+        $latest = $available['releases'][$project_data['latest_dev']];
+        if (empty($project_data['datestamp'])) {
+          $project_data['status'] = DRUSH_UPDATESTATUS_NOT_CHECKED;
+        }
+        elseif (($project_data['datestamp'] + 100 > $latest['date'])) {
+          $project_data['status'] = DRUSH_UPDATESTATUS_CURRENT;
+        }
+        else {
+          $project_data['status'] = DRUSH_UPDATESTATUS_NOT_CURRENT;
+        }
+        break;
+
+      default:
+        $project_data['status'] = DRUSH_UPDATESTATUS_UNKNOWN;
+    }
+  }
+}
+