Merge pull request #7886 from sarehag/CRM-18114
[civicrm-core.git] / CRM / Utils / VersionCheck.php
index f0e956a5d7af724f3e5f97c67c16192587eb3044..9b51ed6e948e0af234ddbf82a781ce3c91c97d06 100644 (file)
  */
 class CRM_Utils_VersionCheck {
   const
-    PINGBACK_URL = 'http://latest.civicrm.org/stable.php?format=json',
-    // relative to $civicrm_root
-    LOCALFILE_NAME = '[civicrm.root]/civicrm-version.php',
-    // relative to $config->uploadDir
-    CACHEFILE_NAME = '[civicrm.files]/persist/version-info-cache.json';
+    CACHEFILE_NAME = 'version-info-cache.json',
+    // after this length of time we fall back on poor-man's cron (7+ days)
+    CACHEFILE_EXPIRE = 605000;
 
   /**
    * The version of the current (local) installation
@@ -64,6 +62,16 @@ class CRM_Utils_VersionCheck {
    */
   public $isInfoAvailable;
 
+  /**
+   * @var array
+   */
+  public $cronJob = array();
+
+  /**
+   * @var string
+   */
+  public $pingbackUrl = 'http://latest.civicrm.org/stable.php?format=json';
+
   /**
    * Pingback params
    *
@@ -76,26 +84,52 @@ class CRM_Utils_VersionCheck {
    *
    * @var string
    */
-  protected $cacheFile;
+  public $cacheFile;
 
   /**
    * Class constructor.
    */
   public function __construct() {
-    // Populate local version
-    $localFile = Civi::paths()->getPath(self::LOCALFILE_NAME);
-    if (file_exists($localFile)) {
-      require_once $localFile;
-    }
-    if (function_exists('civicrmVersion')) {
-      $info = civicrmVersion();
-      $this->localVersion = trim($info['version']);
-      $this->localMajorVersion = $this->getMajorVersion($this->localVersion);
-    }
+    $this->localVersion = CRM_Utils_System::version();
+    $this->localMajorVersion = $this->getMajorVersion($this->localVersion);
+    $this->cacheFile = CRM_Core_Config::singleton()->uploadDir . self::CACHEFILE_NAME;
+  }
+
+  /**
+   * Self-populates version info
+   *
+   * @throws \Exception
+   */
+  public function initialize() {
+    $this->getJob();
 
     // Populate remote $versionInfo from cache file
-    $this->cacheFile = Civi::paths()->getPath(self::CACHEFILE_NAME);
     $this->isInfoAvailable = $this->readCacheFile();
+
+    // Poor-man's cron fallback if scheduled job is enabled but has failed to run
+    $expiryTime = time() - self::CACHEFILE_EXPIRE;
+    if (!empty($this->cronJob['is_active']) &&
+      (!$this->isInfoAvailable || filemtime($this->cacheFile) < $expiryTime)
+    ) {
+      // First try updating the files modification time, for 2 reasons:
+      //  - if the file is not writeable, this saves the trouble of pinging back
+      //  - if the remote server is down, this will prevent an immediate retry
+      if (touch($this->cacheFile) === FALSE) {
+        throw new Exception('File not writable');
+      }
+      $this->fetch();
+    }
+  }
+
+  /**
+   * Sets $versionInfo
+   *
+   * @param $info
+   */
+  public function setVersionInfo($info) {
+    $this->versionInfo = (array) $info;
+    // Sort version info in ascending order for easier comparisons
+    ksort($this->versionInfo, SORT_NUMERIC);
   }
 
   /**
@@ -360,14 +394,15 @@ class CRM_Utils_VersionCheck {
       ),
     );
     $ctx = stream_context_create($params);
-    $rawJson = file_get_contents(self::PINGBACK_URL, FALSE, $ctx);
+    $rawJson = file_get_contents($this->pingbackUrl, FALSE, $ctx);
     $versionInfo = $rawJson ? json_decode($rawJson, TRUE) : NULL;
     // If we couldn't fetch or parse the data $versionInfo will be NULL
     // Otherwise it will be an array and we'll cache it.
     // Note the array may be empty e.g. in the case of a pre-alpha with no releases
-    if ($versionInfo !== NULL) {
+    $this->isInfoAvailable = $versionInfo !== NULL;
+    if ($this->isInfoAvailable) {
       $this->writeCacheFile($rawJson);
-      $this->versionInfo = $versionInfo;
+      $this->setVersionInfo($versionInfo);
     }
   }
 
@@ -376,9 +411,7 @@ class CRM_Utils_VersionCheck {
    */
   private function readCacheFile() {
     if (file_exists($this->cacheFile)) {
-      $this->versionInfo = (array) json_decode(file_get_contents($this->cacheFile), TRUE);
-      // Sort version info in ascending order for easier comparisons
-      ksort($this->versionInfo, SORT_NUMERIC);
+      $this->setVersionInfo(json_decode(file_get_contents($this->cacheFile), TRUE));
       return TRUE;
     }
     return FALSE;
@@ -387,11 +420,24 @@ class CRM_Utils_VersionCheck {
   /**
    * Save version info to file.
    * @param string $contents
+   * @throws \Exception
    */
   private function writeCacheFile($contents) {
-    $fp = fopen($this->cacheFile, 'w');
-    fwrite($fp, $contents);
-    fclose($fp);
+    if (file_put_contents($this->cacheFile, $contents) === FALSE) {
+      throw new Exception('File not writable');
+    }
+  }
+
+  /**
+   * Lookup version_check scheduled job
+   */
+  private function getJob() {
+    $jobs = civicrm_api3('Job', 'get', array(
+      'sequential' => 1,
+      'api_action' => "version_check",
+      'api_entity' => "job",
+    ));
+    $this->cronJob = CRM_Utils_Array::value(0, $jobs['values'], array());
   }
 
 }