EPESI BIM • Business Information Manager • Cloud CRM/ERP http://epe.si/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

421 lines
13 KiB

  1. <?php
  2. /**
  3. * EPESI Core updater.
  4. *
  5. * @author Adam Bukowski <abukowski@telaxus.com>
  6. * @version 2.0
  7. * @copyright Copyright &copy; 2014, Telaxus LLC
  8. * @license MIT
  9. * @package epesi-base
  10. */
  11. defined("_VALID_ACCESS") || define("_VALID_ACCESS", true);
  12. class EpesiUpdatePackage
  13. {
  14. private $file;
  15. private $zip;
  16. public function __construct($file)
  17. {
  18. $this->file = $file;
  19. $this->zip = new ZipArchive();
  20. $status = $this->zip->open($file);
  21. if ($status !== true) {
  22. throw new ErrorException(__('Zip open error: %s', array($status)));
  23. }
  24. }
  25. public function __destruct()
  26. {
  27. $this->zip->close();
  28. }
  29. public function get_file()
  30. {
  31. return $this->file;
  32. }
  33. public function files_not_writable()
  34. {
  35. $problems = array();
  36. for ($i = 0; $i < $this->zip->numFiles; $i++) {
  37. $stat = $this->zip->statIndex($i);
  38. $f = './' . $stat['name'];
  39. if (file_exists($f)) {
  40. if (!is_writable($f)) {
  41. $problems[] = $f;
  42. }
  43. } else {
  44. $up_f = basename($f);
  45. if (file_exists($up_f) && !is_writable($up_f)) {
  46. $problems[] = $up_f;
  47. }
  48. }
  49. }
  50. return $problems;
  51. }
  52. public function extract()
  53. {
  54. $success = $this->zip->extractTo('./');
  55. return $success;
  56. }
  57. public static function system_requirements()
  58. {
  59. return class_exists('ZipArchive');
  60. }
  61. public static function package()
  62. {
  63. if (!self::system_requirements()) {
  64. return false;
  65. }
  66. $possible_files = glob('epesi-*.ei.zip');
  67. if (is_array($possible_files) && count($possible_files)) {
  68. rsort($possible_files);
  69. return new self($possible_files[0]);
  70. }
  71. return false;
  72. }
  73. }
  74. class EpesiUpdate
  75. {
  76. public function run()
  77. {
  78. $this->load_epesi();
  79. if ($this->check_user()) {
  80. if (!epesi_requires_update()) {
  81. if ($this->handle_update_package() == false) {
  82. $this->version_up_to_date();
  83. }
  84. }
  85. $this->update_process();
  86. } else {
  87. $this->require_admin_login();
  88. }
  89. }
  90. protected function load_epesi()
  91. {
  92. $this->CLI = (php_sapi_name() == 'cli');
  93. if ($this->CLI) {
  94. // allow to define DATA directory for CLI in argument
  95. if(isset($argv)) {
  96. define('EPESI_DIR','/');
  97. if (isset($argv[1])) {
  98. define('DATA_DIR', $argv[1]);
  99. }
  100. }
  101. }
  102. define('CID', false);
  103. require_once('include.php');
  104. ModuleManager::load_modules();
  105. Base_LangCommon::load();
  106. $this->system_version = Variable::get('version');
  107. $this->current_version = EPESI_VERSION;
  108. }
  109. protected function quit($msg)
  110. {
  111. if ($this->CLI) {
  112. die($msg . "\n");
  113. } else {
  114. $this->body($msg);
  115. die();
  116. }
  117. }
  118. protected function check_user()
  119. {
  120. if ($this->CLI) {
  121. Base_AclCommon::set_sa_user();
  122. if (!Base_AclCommon::i_am_sa()) {
  123. $this->quit('No proper admin user.');
  124. }
  125. }
  126. return Base_AclCommon::i_am_sa();
  127. }
  128. public function version_up_to_date()
  129. {
  130. $msg = __('Your EPESI does not require update');
  131. if ($this->CLI) {
  132. print ($msg . "\n");
  133. print (__('Update procedure forced') . "\n");
  134. } else {
  135. $this->quit($msg);
  136. }
  137. }
  138. protected function require_admin_login()
  139. {
  140. $msg = '<p><strong>' . __("You need to be logged in as super admin use this script.") . '</strong></p>';
  141. $msg .= $this->login_form();
  142. $this->quit($msg);
  143. }
  144. protected function login_form()
  145. {
  146. if (Base_AclCommon::i_am_user() && !Base_AclCommon::i_am_sa()) {
  147. Base_User_LoginCommon::logout();
  148. }
  149. $form = SimpleLogin::form();
  150. return "<p>$form</p>";
  151. }
  152. protected function handle_update_package()
  153. {
  154. if ($this->CLI) {
  155. return false;
  156. }
  157. $package = EpesiUpdatePackage::package();
  158. if ($package) {
  159. $action = & $_GET['package'];
  160. if ($action) {
  161. $files_not_writable = $package->files_not_writable();
  162. if (empty($files_not_writable)) {
  163. $this->turn_on_maintenance_mode();
  164. if ($package->extract()) {
  165. $this->redirect(array());
  166. } else {
  167. $this->quit(__('Extract error occured'));
  168. }
  169. } else {
  170. $msg = '<p><strong>' . __('Files with bad permissions:') . '</strong></p>';
  171. foreach ($files_not_writable as $file) {
  172. $msg .= "$file</br>";
  173. }
  174. $this->quit($msg);
  175. }
  176. } else {
  177. $header = __('Easy update package found!');
  178. $current_ver = __('Your current EPESI version') . ': <strong>' . $this->current_version . '</strong>';
  179. $text_p = __('Package') . ': <strong>' . $package->get_file() . '</strong>';
  180. $warning_message = __('All core files will be overwritten!') . '<br/><br/>'
  181. . __('If you have changed any of those files, then all custom modifications will be lost.');
  182. $info_message = __('Custom modules and your data will be preserved.');
  183. $msg = "<p><strong>$header</strong></p><p>$current_ver</p><p>$text_p</p>";
  184. $msg .= "<p style=\"color: red; font-weight: bold\">$warning_message</p>";
  185. $msg .= "<p style=\"font-weight: bold\">$info_message</p>";
  186. $msg .= '<p><a class="button" href="?package=extract">' . __('Extract!') . '</a></p>';
  187. $this->quit($msg);
  188. }
  189. }
  190. return false;
  191. }
  192. protected function update_process()
  193. {
  194. @set_time_limit(0);
  195. // console
  196. if ($this->CLI) {
  197. PatchUtil::disable_time_management();
  198. $this->perform_update_start();
  199. $this->perform_update_patches(false);
  200. $this->perform_update_end();
  201. $this->quit(__('Done'));
  202. }
  203. // browser
  204. $up = & $_GET['up'];
  205. if ($up == 'start') {
  206. $this->perform_update_start();
  207. $this->redirect(array('up' => 'patches'));
  208. } elseif ($up == 'patches') {
  209. $success = $this->perform_update_patches();
  210. if ($success) {
  211. $this->redirect(array('up' => 'end'));
  212. }
  213. } elseif ($up == 'end') {
  214. $this->perform_update_end();
  215. $this->redirect(get_epesi_url());
  216. } else {
  217. $this->update_body();
  218. }
  219. }
  220. protected function redirect($url_or_get)
  221. {
  222. if (is_string($url_or_get)) {
  223. $location = $url_or_get;
  224. } else {
  225. $location = $_SERVER['PHP_SELF'];
  226. if (count($url_or_get)) {
  227. $location .= '?' . http_build_query($url_or_get);
  228. }
  229. }
  230. header("Location: $location");
  231. die();
  232. }
  233. protected function update_msg()
  234. {
  235. $msg = __('Update EPESI from version %s to %s.', array($this->system_version, $this->current_version));
  236. return "<p>$msg</p>";
  237. }
  238. protected function update_process_info_msg()
  239. {
  240. $do_not_close = __('Please do not close this window until process will be fully finished.');
  241. $url_text = __('help file');
  242. $url = get_epesi_url() . '/docs/UPDATE.md';
  243. $url = htmlspecialchars($url);
  244. $link = "<a href=\"$url\" target=\"_blank\">$url_text</a>";
  245. $info = __('Your browser drives update process. For more information read %s', array($link));
  246. $msg = "<p><strong>$do_not_close</strong></p><p>$info</p>";
  247. return "$msg";
  248. }
  249. protected function update_body()
  250. {
  251. $msg = $this->update_msg();
  252. $msg .= $this->update_process_info_msg();
  253. $msg .= ' <a class="button" href="?up=start">' . __('Update!') . '</a>';
  254. $this->quit($msg);
  255. }
  256. protected function turn_on_maintenance_mode()
  257. {
  258. if (MaintenanceMode::is_on()) return;
  259. $msg = __('EPESI is currently updating. Please wait or contact your system administrator.');
  260. if ($this->CLI) {
  261. MaintenanceMode::turn_on($msg);
  262. } else {
  263. MaintenanceMode::turn_on_with_cookie($msg);
  264. }
  265. }
  266. protected function perform_update_start()
  267. {
  268. $this->turn_on_maintenance_mode();
  269. //restore innodb tables in case of db reimport
  270. if (DB::is_mysql()) {
  271. $tbls = DB::MetaTables('TABLE', true);
  272. foreach ($tbls as $t) {
  273. $tbl = DB::GetRow('SHOW CREATE TABLE ' . $t);
  274. if (!isset($tbl[1]) || preg_match('/ENGINE=myisam/i', $tbl[1])) {
  275. DB::Execute('ALTER TABLE ' . $t . ' ENGINE = INNODB');
  276. }
  277. }
  278. }
  279. }
  280. protected function perform_update_patches($browser = true)
  281. {
  282. $this->turn_on_maintenance_mode();
  283. $patches = PatchUtil::apply_new(true);
  284. if ($browser) {
  285. $success = PatchUtil::all_successful($patches);
  286. if (!$success) {
  287. $msg = self::format_patches_msg($patches);
  288. $this->body($msg);
  289. }
  290. return $success;
  291. }
  292. }
  293. protected function format_patches_msg($patches)
  294. {
  295. $msg = "<h1>" . __('Patches to apply') . ":</h1>";
  296. $msg .= "<p>" . __('Last refresh') . ' - ' . date('Y-m-d H:i:s') . "</p>";
  297. $msg .= '<table>';
  298. // table header
  299. $format = "<tr><th>%s</th><th>%s</th><th>%s</th></tr>\n";
  300. $msg .= sprintf($format, __('Module'), __('Patch'), __('Status'));
  301. $format = "<tr><td>%s</td><td>%s</td><td style=\"text-align: center; font-size: 0.8em; color: gray\">%s</td></tr>\n";
  302. /** @var Patch $patch */
  303. foreach ($patches as $patch) {
  304. // show only awaiting or processed one
  305. if ($patch->get_apply_status() == Patch::STATUS_SUCCESS) {
  306. continue;
  307. }
  308. $status = __('pending');
  309. if ($patch->get_apply_status() == Patch::STATUS_TIMEOUT) {
  310. $status = '<img src="images/loader.gif" alt="Processing..." width="128" height="5" border="0">';
  311. }
  312. if (($user_message = $patch->get_user_message()) != null) {
  313. $status .= "<div>$user_message</div>";
  314. }
  315. $msg .= sprintf($format, $patch->get_module(), $patch->get_short_description(), $status);
  316. }
  317. $msg .= '</table>';
  318. $msg .= '<script type="text/javascript">location.reload(true)</script>';
  319. return $msg;
  320. }
  321. protected function perform_update_end()
  322. {
  323. $this->turn_on_maintenance_mode();
  324. Base_ThemeCommon::themeup();
  325. Base_LangCommon::update_translations();
  326. ModuleManager::create_load_priority_array();
  327. Variable::set('version', EPESI_VERSION);
  328. MaintenanceMode::turn_off();
  329. }
  330. protected function body($html)
  331. {
  332. ?>
  333. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  334. <html>
  335. <head>
  336. <meta content="text/html; charset=utf-8" http-equiv="content-type">
  337. <title><?php print(EPESI); ?> update</title>
  338. <link href="setup.css" type="text/css" rel="stylesheet"/>
  339. <meta name="robots" content="NOINDEX, NOARCHIVE">
  340. </head>
  341. <body>
  342. <table id="banner" border="0" cellpadding="0" cellspacing="0">
  343. <tr>
  344. <td class="image">&nbsp;</td>
  345. <td class="back">&nbsp;</td>
  346. </tr>
  347. </table>
  348. <br>
  349. <center>
  350. <table id="main" border="0" cellpadding="0" cellspacing="0">
  351. <tr>
  352. <td>
  353. <center>
  354. <?php print $html; ?>
  355. </center>
  356. </td>
  357. </tr>
  358. </table>
  359. </center>
  360. <br>
  361. <center>
  362. <span class="footer">Copyright &copy; 2014 &bull; <a
  363. href="http://www.telaxus.com">Telaxus LLC</a></span>
  364. <br>
  365. <p><a href="http://www.epesi.org"><img
  366. src="images/epesi-powered.png" alt="image"
  367. border="0"></a></p>
  368. </center>
  369. </body>
  370. </html>
  371. <?php
  372. }
  373. protected $CLI;
  374. protected $system_version;
  375. protected $current_version;
  376. }
  377. $x = new EpesiUpdate();
  378. $x->run();