All Data Structures Functions Variables Pages
UpgradeUtil.php
1 <?php
2  /*********************************************************************************
3  * Zurmo is a customer relationship management program developed by
4  * Zurmo, Inc. Copyright (C) 2017 Zurmo Inc.
5  *
6  * Zurmo is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  *
13  * Zurmo is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  *
23  * You can contact Zurmo, Inc. with a mailing address at 27 North Wacker Drive
24  * Suite 370 Chicago, IL 60606. or at email address contact@zurmo.com.
25  *
26  * The interactive user interfaces in original and modified versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  *
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the Zurmo
32  * logo and Zurmo copyright notice. If the display of the logo is not reasonably
33  * feasible for technical reasons, the Appropriate Legal Notices must display the words
34  * "Copyright Zurmo Inc. 2017. All rights reserved".
35  ********************************************************************************/
36 
38  {
39  const UPGRADE_STATE_KEY = 'zurmoUpgrade';
40 
51  public static function runPart1(MessageStreamer $messageStreamer, $doNotlAlterFiles = false)
52  {
53  try
54  {
55  self::isApplicationInUpgradeMode();
56  self::checkIfThereAreJobsInProcess();
57  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
58  self::clearCache();
59  $messageStreamer->add(Zurmo::t('Core', 'Checking permissions, files, upgrade version....'));
60  $messageLogger = new MessageLogger($messageStreamer);
61 
62  self::setUpgradeState('zurmoUpgradeTimestamp', time());
63  self::checkPermissions();
64  self::checkIfZipExtensionIsLoaded();
65  self::setCurrentZurmoVersion();
66  $upgradeZipFile = self::checkForUpgradeZip();
67  $upgradeExtractPath = self::unzipUpgradeZip($upgradeZipFile);
68  self::setUpgradeState('zurmoUpgradeFolderPath', $upgradeExtractPath);
69 
70  $configuration = self::checkManifestIfVersionIsOk($upgradeExtractPath);
71  $messageStreamer->add(Zurmo::t('Core', 'Check completed.'));
72  $messageStreamer->add(Zurmo::t('Core', 'Loading UpgraderComponent.'));
73  self::loadUpgraderComponent($upgradeExtractPath, $messageLogger);
74  $messageStreamer->add(Zurmo::t('Core', 'UpgraderComponent loaded.'));
75  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
76  self::clearCache();
77 
78  $messageStreamer->add(Zurmo::t('Core', 'Altering configuration files.'));
79  $pathToConfigurationFolder = COMMON_ROOT . DIRECTORY_SEPARATOR . 'protected' . DIRECTORY_SEPARATOR . 'config';
80  self::processBeforeConfigFiles();
81  self::processConfigFiles($pathToConfigurationFolder);
82  self::processAfterConfigFiles();
83 
84  if (!$doNotlAlterFiles)
85  {
86  $messageStreamer->add(Zurmo::t('Core', 'Copying files.'));
87  self::processBeforeFiles();
88  self::processFiles($upgradeExtractPath, $configuration);
89  self::processAfterFiles();
90  }
91 
92  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
93  self::clearCache();
94  $messageStreamer->add(Zurmo::t('Core', 'Part 1 complete.'));
95  }
96  catch (CException $e)
97  {
98  $messageStreamer->add(Zurmo::t('Core', 'Error during upgrade!'));
99  $messageStreamer->add($e->getMessage());
100  $messageStreamer->add(Zurmo::t('Core', 'Please fix error(s) and try again, or restore your database/files.'));
101  Yii::app()->end();
102  }
103  }
104 
114  public static function runPart2(MessageStreamer $messageStreamer)
115  {
116  try
117  {
118  self::isApplicationInUpgradeMode();
119  self::checkIfThereAreJobsInProcess();
120  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
121  self::clearCache();
122  $upgradeExtractPath = self::getUpgradeState('zurmoUpgradeFolderPath');
123  $messageLogger = new MessageLogger($messageStreamer);
124 
125  $messageStreamer->add(Zurmo::t('Core', 'Loading UpgraderComponent.'));
126  self::loadUpgraderComponent($upgradeExtractPath, $messageLogger);
127  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
128  self::clearCache();
129  $messageStreamer->add(Zurmo::t('Core', 'Running tasks before updating schema.'));
130  self::processBeforeUpdateSchema();
131  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
132  self::clearCache();
133  $messageStreamer->add(Zurmo::t('Core', 'Updating schema.'));
134  self::processUpdateSchema($messageLogger);
135  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
136  self::clearCache();
137  $messageStreamer->add(Zurmo::t('Core', 'Running tasks after schema is updated.'));
138  self::processAfterUpdateSchema();
139  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
140  self::clearCache();
141  $messageStreamer->add(Zurmo::t('Core', 'Clearing assets and runtime folders.'));
142  self::clearAssetsAndRunTimeItems();
143  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
144  self::clearCache();
145  $messageStreamer->add(Zurmo::t('Core', 'Processing final touches.'));
146  self::processFinalTouches();
147  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
148  self::clearCache();
149  $messageStreamer->add(Zurmo::t('Core', 'Removing upgrade files.'));
150  self::removeUpgradeFiles($upgradeExtractPath);
151  self::unsetUpgradeState();
152  $messageStreamer->add(Zurmo::t('Core', 'Clearing cache.'));
153  self::clearCache();
154  $messageStreamer->add(Zurmo::t('Core', 'Upgrade process completed.'));
155  }
156  catch (CException $e)
157  {
158  $messageStreamer->add(Zurmo::t('Core', 'Error during upgrade!'));
159  $messageStreamer->add($e->getMessage());
160  $messageStreamer->add(Zurmo::t('Core', 'Please fix error(s) and try again, or restore your database/files.'));
161  Yii::app()->end();
162  }
163  }
164 
165  /*
166  * Check if application is in maintenance mode
167  * @throws NotSupportedException
168  * @return boolean
169  */
170  public static function isApplicationInUpgradeMode()
171  {
172  if (!Yii::app()->isApplicationInMaintenanceMode())
173  {
174  $message = Zurmo::t('Core', 'Application is not in maintenance mode. Please edit perInstance.php file, and set "$maintenanceMode = true;"');
175  throw new NotSupportedException($message);
176  }
177  return true;
178  }
179 
180  public static function checkIfThereAreJobsInProcess()
181  {
182  $jobsInProcess = JobInProcess::getAll();
183  if (!empty($jobsInProcess))
184  {
185  $message = Zurmo::t('Core', 'There are jobs that are currently in progress.') . "\n";
186  $message .= Zurmo::t('Core', 'Please set maintenanceMode to true and wait for these jobs to complete, and run this command again') . "\n";
187  $message .= Zurmo::t('Core', 'Here is list of ative jobs:') . "\n";
188  foreach ($jobsInProcess as $job)
189  {
190  $message .= Zurmo::t('Core', '{staredAt}: {type}',
191  array('{staredAt}' => $job->createdDateTime, '{type}' => $job->type)) . "\n";
192  }
193  throw new NotSupportedException($message);
194  }
195  return true;
196  }
197 
202  public static function getUpgradeFolderPath()
203  {
204  return Yii::app()->getRuntimePath() . DIRECTORY_SEPARATOR . 'upgrade';
205  }
206 
212  protected static function checkPermissions()
213  {
214  // All files/folders must be writeable by user that runs upgrade process.
215  $nonWriteableFilesOrFolders = FileUtil::getNonWriteableFilesOrFolders(COMMON_ROOT);
216  if (!empty($nonWriteableFilesOrFolders))
217  {
218  $message = Zurmo::t('Core', 'Not all files and folders are writeable by upgrade user. Please make these files or folders writeable:');
219  foreach ($nonWriteableFilesOrFolders as $nonWriteableFileOrFolder)
220  {
221  $message .= $nonWriteableFileOrFolder . "\n";
222  }
223  throw new FileNotWriteableException($message);
224  }
225  return true;
226  }
227 
233  protected static function checkIfZipExtensionIsLoaded()
234  {
235  $isZipExtensionInstalled = InstallUtil::checkZip();
236  if (!$isZipExtensionInstalled)
237  {
238  $message = Zurmo::t('Core', 'Zip PHP extension is required by upgrade process, please install it.');
239  throw new NotSupportedException($message);
240  }
241  return true;
242  }
243 
247  protected static function setCurrentZurmoVersion()
248  {
249  $currentZurmoVersion = join('.', array(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION));
250  self::setUpgradeState('zurmoVersionBeforeUpgrade', $currentZurmoVersion);
251  }
252 
259  protected static function checkForUpgradeZip()
260  {
261  $numberOfZipFiles = 0;
262  $upgradePath = self::getUpgradeFolderPath();
263  if (!is_dir($upgradePath))
264  {
265  $message = Zurmo::t('Core', 'Please upload upgrade zip file to runtime/upgrade folder.');
266  throw new NotFoundException($message);
267  }
268 
269  $handle = opendir($upgradePath);
270  while (($item = readdir($handle)) !== false)
271  {
272  $filePath = explode('.', $item);
273  if (end($filePath) == 'zip')
274  {
275  $upgradeZipFile = $upgradePath . DIRECTORY_SEPARATOR . $item;
276  $numberOfZipFiles++;
277  }
278  }
279 
280  if ($numberOfZipFiles != 1)
281  {
282  closedir($handle);
283  $message = Zurmo::t('Core', 'More then one zip file exists in runtime/upgrade folder. ' .
284  'Please delete them all except the one that you want to use for the upgrade.');
285  throw new NotSupportedException($message);
286  }
287  closedir($handle);
288  return $upgradeZipFile;
289  }
290 
297  protected static function unzipUpgradeZip($upgradeZipFilePath)
298  {
299  // Remove extracted files, if they already exists.
300  $fileInfo = pathinfo($upgradeZipFilePath);
301  FileUtil::deleteDirectoryRecursive($fileInfo['dirname'], false, array($fileInfo['basename'], 'index.html'));
302 
303  $isExtracted = false;
304  $zip = new ZipArchive();
305  $upgradeExtractPath = Yii::app()->getRuntimePath() . DIRECTORY_SEPARATOR . "upgrade";
306 
307  if ($zip->open($upgradeZipFilePath) === true)
308  {
309  $isExtracted = $zip->extractTo($upgradeExtractPath);
310  $zip->close();
311  }
312  if (!$isExtracted)
313  {
314  $message = Zurmo::t('Core', 'There was an error during the extraction process of {zipFilePath}', array('{zipFilePath}' => $upgradeZipFilePath));
315  $message .= Zurmo::t('Core', 'Please check if the file is a valid zip archive.');
316  throw new NotSupportedException($message);
317  }
318  return $upgradeExtractPath . DIRECTORY_SEPARATOR . $fileInfo['filename'];
319  }
320 
327  protected static function checkManifestIfVersionIsOk($upgradeExtractPath)
328  {
329  require_once($upgradeExtractPath . DIRECTORY_SEPARATOR . 'manifest.php');
330  if (preg_match('/^(\d+)\.(\d+)\.(\d+)$/', $configuration['fromVersion'], $upgradeFromVersionMatches) !== false) // Not Coding Standard
331  {
332  if (preg_match('/^(\d+)\.(\d+)\.?(\d+|\w+)$/', $configuration['toVersion'], $upgradeToVersionMatches) !== false) // Not Coding Standard
333  {
334  $currentZurmoVersion = MAJOR_VERSION . '.' . MINOR_VERSION . '.' . PATCH_VERSION;
335  if (version_compare($currentZurmoVersion, $upgradeFromVersionMatches[0], '>=') &&
336  version_compare($currentZurmoVersion, $upgradeToVersionMatches[0], '<=') )
337  {
338  return $configuration;
339  }
340  else
341  {
342  $message = Zurmo::t('Core', 'This upgrade is for Zurmo ({fromVersion} - {toVersion})',
343  array ('{fromVersion}' => $upgradeFromVersionMatches[0], '{toVersion}' => $upgradeToVersionMatches[0]));
344  $message .= Zurmo::t('Core', 'Installed Zurmo version is: {currentZurmoVersion}',
345  array('{currentZurmoVersion}' => $currentZurmoVersion));
346  throw new NotSupportedException($message);
347  }
348  }
349  else
350  {
351  $message = Zurmo::t('Core', 'Could not extract upgrade "to version" in the manifest file.');
352  throw new NotSupportedException($message);
353  }
354  }
355  else
356  {
357  $message = Zurmo::t('Core', 'Could not extract upgrade "from version" in the manifest file.');
358  throw new NotSupportedException($message);
359  }
360  }
361 
366  protected static function loadUpgraderComponent($upgradeExtractPath, MessageLogger $messageLogger)
367  {
368  if (file_exists($upgradeExtractPath . DIRECTORY_SEPARATOR . 'UpgraderComponent.php'))
369  {
370  require_once($upgradeExtractPath . DIRECTORY_SEPARATOR . 'UpgraderComponent.php');
371 
372  $upgraderComponent = Yii::createComponent(
373  array('class' => 'UpgraderComponent', 'messageLogger' => $messageLogger)
374  );
375  Yii::app()->setComponent('upgrader', $upgraderComponent);
376  }
377  else
378  {
379  $message = Zurmo::t('Core', 'Upgrade file is missing.');
380  throw new NotSupportedException($message);
381  }
382  }
383 
387  protected static function clearCache()
388  {
389  ForgetAllCacheUtil::forgetAllCaches();
390  }
391 
395  protected static function processBeforeConfigFiles()
396  {
397  Yii::app()->upgrader->processBeforeConfigFiles();
398  }
399 
403  protected static function processConfigFiles($pathToConfigurationFolder)
404  {
405  Yii::app()->upgrader->processConfigFiles($pathToConfigurationFolder);
406  }
407 
411  protected static function processAfterConfigFiles()
412  {
413  Yii::app()->upgrader->processAfterConfigFiles();
414  }
415 
419  protected static function processBeforeFiles()
420  {
421  Yii::app()->upgrader->processBeforeFiles();
422  }
423 
429  protected static function processFiles($upgradeExtractPath, $configuration)
430  {
431  $source = $upgradeExtractPath . DIRECTORY_SEPARATOR . 'filesToUpload';
432  $destination = COMMON_ROOT . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR;
433  Yii::app()->upgrader->processFiles($source, $destination, $configuration);
434  }
435 
439  protected static function processAfterFiles()
440  {
441  Yii::app()->upgrader->processAfterFiles();
442  }
443 
447  protected static function processBeforeUpdateSchema()
448  {
449  Yii::app()->upgrader->processBeforeUpdateSchema();
450  }
451 
455  protected static function processUpdateSchema($messageLogger)
456  {
457  Yii::app()->upgrader->processUpdateSchema($messageLogger);
458  }
459 
463  protected static function processAfterUpdateSchema()
464  {
465  Yii::app()->upgrader->processAfterUpdateSchema();
466  }
467 
471  protected static function clearAssetsAndRunTimeItems()
472  {
473  Yii::app()->upgrader->clearAssetsAndRunTimeItems();
474  }
475 
479  protected static function processFinalTouches()
480  {
481  Yii::app()->upgrader->processFinalTouches();
482  }
483 
484  protected static function removeUpgradeFiles($upgradeExtractPath)
485  {
486  FileUtil::deleteDirectoryRecursive($upgradeExtractPath, true);
487  }
488 
495  public static function setUpgradeState($key, $value)
496  {
497  $statePersister = Yii::app()->getStatePersister();
498  $state = $statePersister->load();
499  $state[self::UPGRADE_STATE_KEY][$key] = $value;
500  $statePersister->save($state);
501  return true;
502  }
503 
509  public static function getUpgradeState($key)
510  {
511  $statePersister = Yii::app()->getStatePersister();
512  $state = $statePersister->load();
513  if (isset($state[self::UPGRADE_STATE_KEY][$key]))
514  {
515  return $state[self::UPGRADE_STATE_KEY][$key];
516  }
517  return null;
518  }
519 
523  public static function unsetUpgradeState()
524  {
525  $statePersister = Yii::app()->getStatePersister();
526  $state = $statePersister->load();
527  unset($state[self::UPGRADE_STATE_KEY]);
528  $statePersister->save($state);
529  return true;
530  }
531 
536  public static function isUpgradeStateValid()
537  {
538  $zurmoUpgradeTimestamp = self::getUpgradeState('zurmoUpgradeTimestamp');
539  if ((time() - $zurmoUpgradeTimestamp) > 24 * 60 * 60)
540  {
541  self::unsetUpgradeState();
542  return false;
543  }
544  else
545  {
546  return true;
547  }
548  }
549  }
550 ?>
static checkManifestIfVersionIsOk($upgradeExtractPath)
static runPart2(MessageStreamer $messageStreamer)
static processConfigFiles($pathToConfigurationFolder)
static processFinalTouches()
static processUpdateSchema($messageLogger)
static isUpgradeStateValid()
static processAfterUpdateSchema()
static processAfterFiles()
static processBeforeUpdateSchema()
static processBeforeFiles()
static setCurrentZurmoVersion()
static checkPermissions()
static getUpgradeState($key)
static unsetUpgradeState()
static getUpgradeFolderPath()
static getNonWriteableFilesOrFolders($directory, &$nonWritableItems=array())
Definition: FileUtil.php:99
static clearAssetsAndRunTimeItems()
static checkZip()
static processAfterConfigFiles()
static processFiles($upgradeExtractPath, $configuration)
static processBeforeConfigFiles()
static checkIfZipExtensionIsLoaded()
static unzipUpgradeZip($upgradeZipFilePath)
static checkForUpgradeZip()
static runPart1(MessageStreamer $messageStreamer, $doNotlAlterFiles=false)
Definition: UpgradeUtil.php:51
static getAll($orderBy=null, $sortDescending=false, $modelClassName=null)
static setUpgradeState($key, $value)
static clearCache()
static deleteDirectoryRecursive($directory, $removeDirectoryItself=true, $filesOrFoldersToSkip=array())
Definition: FileUtil.php:167
static loadUpgraderComponent($upgradeExtractPath, MessageLogger $messageLogger)
Generated on Wed Feb 26 2020 07:10:35