PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_zoo/helpers/update.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 371 lines | 165 code | 67 blank | 139 comment | 36 complexity | bf79eb6d4d5e2541b4d93a5ec0791bd7 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * @package com_zoo
  4. * @author YOOtheme http://www.yootheme.com
  5. * @copyright Copyright (C) YOOtheme GmbH
  6. * @license http://www.gnu.org/licenses/gpl.html GNU/GPL
  7. */
  8. /**
  9. * Update helper class.
  10. *
  11. * @package Component.Helpers
  12. * @since 2.0
  13. */
  14. class UpdateHelper extends AppHelper {
  15. /**
  16. * The update cache
  17. *
  18. * @var AppCache|false
  19. */
  20. protected $_cache;
  21. /**
  22. * Checks if ZOO needs to be updated.
  23. *
  24. * @return boolean true if ZOO needs to be updated
  25. * @since 2.0
  26. */
  27. public function required() {
  28. $updates = $this->getRequiredUpdates();
  29. return !empty($updates);
  30. }
  31. /**
  32. * Return required update versions.
  33. *
  34. * @return array versions of required updates
  35. * @since 2.0
  36. */
  37. public function getRequiredUpdates() {
  38. // get current version
  39. $current_version = $this->getVersion();
  40. // find required updates
  41. if ($files = $this->app->path->files('updates:', false, '/^\d+.*\.php$/')) {
  42. $files = array_map(create_function('$file', 'return basename($file, ".php");'), array_filter($files, create_function('$file', 'return version_compare("'.$current_version.'", basename($file, ".php")) < 0;')));
  43. usort($files, create_function('$a, $b', 'return version_compare($a, $b);'));
  44. }
  45. return $files;
  46. }
  47. /**
  48. * Get preupdate notifications.
  49. *
  50. * @return array messages
  51. * @since 2.0
  52. */
  53. public function getNotifications() {
  54. // check if update is required
  55. if (!$this->required()) {
  56. return $this->_createResponse('No update required.', false, false);
  57. }
  58. // get current version
  59. $current_version = $this->getVersion();
  60. $notifications = array();
  61. // find and run the next update
  62. foreach ($this->getRequiredUpdates() as $version) {
  63. if ((version_compare($version, $current_version) > 0)) {
  64. $class = 'Update'.str_replace('.', '', $version);
  65. $this->app->loader->register($class, "updates:$version.php");
  66. if (class_exists($class)) {
  67. // make sure class implemnts iUpdate interface
  68. $r = new ReflectionClass($class);
  69. if ($r->isSubclassOf('iUpdate') && !$r->isAbstract()) {
  70. // run the update
  71. $notification = $r->newInstance()->getNotifications($this->app);
  72. if (is_array($notification)) {
  73. $notifications = array_merge($notifications, $notification);
  74. }
  75. }
  76. }
  77. }
  78. }
  79. return $notifications;
  80. }
  81. /**
  82. * Performs the next update.
  83. *
  84. * @return array response
  85. * @since 2.0
  86. */
  87. public function run() {
  88. // check if update is required
  89. if (!$this->required()) {
  90. return $this->_createResponse('No update required.', false, false);
  91. }
  92. // get current version
  93. $current_version = $this->getVersion();
  94. // find and run the next update
  95. $updates = $this->getRequiredUpdates();
  96. foreach ($updates as $version) {
  97. if ((version_compare($version, $current_version) > 0)) {
  98. $class = 'Update'.str_replace('.', '', $version);
  99. $this->app->loader->register($class, "updates:$version.php");
  100. if (class_exists($class)) {
  101. // make sure class implemnts iUpdate interface
  102. $r = new ReflectionClass($class);
  103. if ($r->isSubclassOf('iUpdate') && !$r->isAbstract()) {
  104. try {
  105. // run the update
  106. $r->newInstance()->run($this->app);
  107. } catch (Exception $e) {
  108. return $this->_createResponse("Error during update! ($e)", true, false);
  109. }
  110. // set current version
  111. $version_string = $version;
  112. if (!$required = count($updates) > 1) {
  113. if (($xml = simplexml_load_file($this->app->path->path('component.admin:zoo.xml'))) && (string) $xml->name == 'ZOO') {
  114. $version_string = (string) $xml->version;
  115. }
  116. }
  117. $this->setVersion($version);
  118. return $this->_createResponse('Successfully updated to version '.$version_string, false, $required);
  119. }
  120. }
  121. }
  122. }
  123. return $this->_createResponse('No update found.', false, false);
  124. }
  125. /**
  126. * Drops and recreates all ZOO database table indexes.
  127. *
  128. * @since 2.0
  129. */
  130. public function refreshDBTableIndexes() {
  131. // sanatize table indexes
  132. if ($this->app->path->path('component.admin:installation/index.sql')) {
  133. $db = $this->app->database;
  134. // read index.sql
  135. $buffer = JFile::read($this->app->path->path('component.admin:installation/index.sql'));
  136. // Create an array of queries from the sql file
  137. jimport('joomla.installer.helper');
  138. $queries = JInstallerHelper::splitSql($buffer);
  139. if (!empty($queries)) {
  140. foreach ($queries as $query) {
  141. // replace table prefixes
  142. $query = $db->replacePrefix($query);
  143. // parse table name
  144. preg_match('/ALTER\s*TABLE\s*`(.*)`/i', $query, $result);
  145. if (count($result) < 2) {
  146. continue;
  147. }
  148. $table = $result[1];
  149. // check if table exists
  150. if (!$db->queryResult('SHOW TABLES LIKE '.$db->Quote($table))) {
  151. continue;
  152. }
  153. // get existing indexes
  154. $indexes = $db->queryObjectList('SHOW INDEX FROM '.$table);
  155. // drop existing indexes
  156. $removed = array();
  157. foreach ($indexes as $index) {
  158. if (in_array($index->Key_name, $removed)) {
  159. continue;
  160. }
  161. if ($index->Key_name != 'PRIMARY') {
  162. $db->query('DROP INDEX '.$index->Key_name.' ON '.$table);
  163. $removed[] = $index->Key_name;
  164. }
  165. }
  166. // add new indexes
  167. $db->query($query);
  168. }
  169. }
  170. }
  171. }
  172. /**
  173. * Gets the current version from versions table.
  174. *
  175. * @return string version
  176. * @since 2.0
  177. */
  178. public function getVersion() {
  179. $cache = $this->getCache();
  180. if (!$version = $cache->get('zoo_version')) {
  181. // make sure versions table is present
  182. $this->app->database->query('CREATE TABLE IF NOT EXISTS '.ZOO_TABLE_VERSION.' (version varchar(255) NOT NULL) ENGINE=MyISAM;');
  183. $version = $this->app->database->queryResult('SELECT version FROM '.ZOO_TABLE_VERSION);
  184. }
  185. $cache->set('zoo_version', $version)->save();
  186. return $version;
  187. }
  188. /**
  189. * Writes the current version in versions table.
  190. *
  191. * @param string $version
  192. *
  193. * @since 2.0
  194. */
  195. public function setVersion($version) {
  196. // remove previous versions
  197. $this->app->database->query('TRUNCATE TABLE '.ZOO_TABLE_VERSION);
  198. // set version
  199. $this->app->database->query('INSERT INTO '.ZOO_TABLE_VERSION.' SET version='.$this->app->database->Quote($version));
  200. $this->getCache()->clear()->save();
  201. }
  202. /**
  203. * Creates an response
  204. *
  205. * @param string $message
  206. * @param string $error
  207. * @param boolean $continue
  208. *
  209. * @return array response
  210. * @since 2.0
  211. */
  212. protected function _createResponse($message, $error, $continue) {
  213. $message = JText::_($message);
  214. return compact('message', 'error', 'continue');
  215. }
  216. /**
  217. * Outputs a message if there is a new update available.
  218. *
  219. * @since 2.0
  220. */
  221. public function available() {
  222. // check for updates
  223. if ($xml = simplexml_load_file($this->app->path->path('component.admin:zoo.xml'))) {
  224. // update check
  225. if ($url = current($xml->xpath('//updateUrl'))) {
  226. // create check url
  227. $url = sprintf('%s?application=%s&version=%s&format=raw', $url, $this->app->joomla->isVersion('1.5') ? 'zoo_j15' : 'zoo_j17', urlencode(current($xml->xpath('//version'))));
  228. // only check once a day
  229. $hash = md5($url.date('Y-m-d'));
  230. $cache = $this->getCache();
  231. $check = $cache->get('check');
  232. $data = $cache->get('data');
  233. $prev_message = @$data['message'];
  234. if ($check != $hash) {
  235. if ($request = $this->app->http->get($url)) {
  236. $check = $hash;
  237. $data = json_decode($request['body'], true);
  238. }
  239. if ($prev_message != @$data['message']) {
  240. $cache->set('hideUpdateNotification', false);
  241. }
  242. }
  243. // decode response and set message
  244. if (!$cache->get('hideUpdateNotification') && @$data['status'] == 'update-available') {
  245. $close = '<span onclick="jQuery.ajax(\''.$this->app->link(array('controller' => 'manager', 'task' => 'hideUpdateNotification')).'\'); jQuery(this).closest(\'ul\').hide();" class="hide-update-notification"></span>';
  246. $this->app->system->application->enqueueMessage(@$data['message'].$close, 'notice');
  247. }
  248. $cache->set('check', $check)->set('data', $data)->save();
  249. }
  250. }
  251. }
  252. /**
  253. * Hides the update notifications for this session
  254. *
  255. * @since 2.0
  256. */
  257. public function hideUpdateNotification() {
  258. $this->getCache()->set('hideUpdateNotification', true)->save();
  259. }
  260. /**
  261. * Returns cache
  262. *
  263. * @return AppCache The update cache
  264. * @since 2.0
  265. */
  266. protected function getCache() {
  267. if (empty($this->_cache)) {
  268. $this->_cache = $this->app->cache->create($this->app->path->path('cache:') . '/zoo_update_cache');
  269. if (!$this->_cache->check()) {
  270. $this->app->system->application->enqueueMessage('Cache not writeable please update the file permissions!', 'warning');
  271. }
  272. }
  273. return $this->_cache;
  274. }
  275. }
  276. /**
  277. * Update interface
  278. *
  279. * @package Component.Helpers
  280. * @since 2.0
  281. */
  282. interface iUpdate {
  283. /**
  284. * Get preupdate notifications.
  285. *
  286. * @param Application $app The application to get the notifications from
  287. * @return array messages
  288. * @since 2.0
  289. */
  290. public function getNotifications($app);
  291. /**
  292. * Performs the update.
  293. *
  294. * @param Application $app The application to get the notifications from
  295. * @return boolean true if updated successful
  296. * @since 2.0
  297. */
  298. public function run($app);
  299. }
  300. /**
  301. * UpdateAppException identifies an Exception in the UpdateHelper class
  302. * @see UpdateHelper
  303. */
  304. class UpdateAppException extends AppException {}