PageRenderTime 24ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/update/update.authorize.inc

http://github.com/drupal/drupal
Pascal | 314 lines | 219 code | 15 blank | 80 comment | 18 complexity | c18156f01cdfb7c2852d3d24989d6d35 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * @file
  4. * Callbacks and related functions invoked by authorize.php to update projects
  5. * on the Drupal site. We use the Batch API to actually update each individual
  6. * project on the site. All of the code in this file is run at a low bootstrap
  7. * level (modules are not loaded), so these functions cannot assume access to
  8. * the rest of the update module code.
  9. */
  10. /**
  11. * Callback invoked by authorize.php to update existing projects.
  12. *
  13. * @param $filetransfer
  14. * The FileTransfer object created by authorize.php for use during this
  15. * operation.
  16. * @param $projects
  17. * A nested array of projects to install into the live webroot, keyed by
  18. * project name. Each subarray contains the following keys:
  19. * - 'project': The canonical project short name.
  20. * - 'updater_name': The name of the Updater class to use for this project.
  21. * - 'local_url': The locally installed location of new code to update with.
  22. */
  23. function update_authorize_run_update($filetransfer, $projects) {
  24. $operations = array();
  25. foreach ($projects as $project => $project_info) {
  26. $operations[] = array(
  27. 'update_authorize_batch_copy_project',
  28. array(
  29. $project_info['project'],
  30. $project_info['updater_name'],
  31. $project_info['local_url'],
  32. $filetransfer,
  33. ),
  34. );
  35. }
  36. $batch = array(
  37. 'title' => t('Installing updates'),
  38. 'init_message' => t('Preparing to update your site'),
  39. 'operations' => $operations,
  40. 'finished' => 'update_authorize_update_batch_finished',
  41. 'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
  42. );
  43. batch_set($batch);
  44. // Invoke the batch via authorize.php.
  45. system_authorized_batch_process();
  46. }
  47. /**
  48. * Callback invoked by authorize.php to install a new project.
  49. *
  50. * @param FileTransfer $filetransfer
  51. * The FileTransfer object created by authorize.php for use during this
  52. * operation.
  53. * @param string $project
  54. * The canonical project short name (e.g. {system}.name).
  55. * @param string $updater_name
  56. * The name of the Updater class to use for installing this project.
  57. * @param string $local_url
  58. * The URL to the locally installed temp directory where the project has
  59. * already been downloaded and extracted into.
  60. */
  61. function update_authorize_run_install($filetransfer, $project, $updater_name, $local_url) {
  62. $operations[] = array(
  63. 'update_authorize_batch_copy_project',
  64. array(
  65. $project,
  66. $updater_name,
  67. $local_url,
  68. $filetransfer,
  69. ),
  70. );
  71. // @todo Instantiate our Updater to set the human-readable title?
  72. $batch = array(
  73. 'title' => t('Installing %project', array('%project' => $project)),
  74. 'init_message' => t('Preparing to install'),
  75. 'operations' => $operations,
  76. // @todo Use a different finished callback for different messages?
  77. 'finished' => 'update_authorize_install_batch_finished',
  78. 'file' => drupal_get_path('module', 'update') . '/update.authorize.inc',
  79. );
  80. batch_set($batch);
  81. // Invoke the batch via authorize.php.
  82. system_authorized_batch_process();
  83. }
  84. /**
  85. * Copy a project to its proper place when authorized with elevated privileges.
  86. *
  87. * @param string $project
  88. * The canonical short name of the project being installed.
  89. * @param string $updater_name
  90. * The name of the Updater class to use for installing this project.
  91. * @param string $local_url
  92. * The URL to the locally installed temp directory where the project has
  93. * already been downloaded and extracted into.
  94. * @param FileTransfer $filetransfer
  95. * The FileTransfer object to use for performing this operation.
  96. * @param array $context
  97. * Reference to an array used for BatchAPI storage.
  98. */
  99. function update_authorize_batch_copy_project($project, $updater_name, $local_url, $filetransfer, &$context) {
  100. // Initialize some variables in the Batch API $context array.
  101. if (!isset($context['results']['log'])) {
  102. $context['results']['log'] = array();
  103. }
  104. if (!isset($context['results']['log'][$project])) {
  105. $context['results']['log'][$project] = array();
  106. }
  107. if (!isset($context['results']['tasks'])) {
  108. $context['results']['tasks'] = array();
  109. }
  110. /**
  111. * The batch API uses a session, and since all the arguments are serialized
  112. * and unserialized between requests, although the FileTransfer object
  113. * itself will be reconstructed, the connection pointer itself will be lost.
  114. * However, the FileTransfer object will still have the connection variable,
  115. * even though the connection itself is now gone. So, although it's ugly, we
  116. * have to unset the connection variable at this point so that the
  117. * FileTransfer object will re-initiate the actual connection.
  118. */
  119. unset($filetransfer->connection);
  120. if (!empty($context['results']['log'][$project]['#abort'])) {
  121. $context['finished'] = 1;
  122. return;
  123. }
  124. $updater = new $updater_name($local_url);
  125. try {
  126. if ($updater->isInstalled()) {
  127. // This is an update.
  128. $tasks = $updater->update($filetransfer);
  129. }
  130. else {
  131. $tasks = $updater->install($filetransfer);
  132. }
  133. }
  134. catch (UpdaterException $e) {
  135. _update_batch_create_message($context['results']['log'][$project], t('Error installing / updating'), FALSE);
  136. _update_batch_create_message($context['results']['log'][$project], $e->getMessage(), FALSE);
  137. $context['results']['log'][$project]['#abort'] = TRUE;
  138. return;
  139. }
  140. _update_batch_create_message($context['results']['log'][$project], t('Installed %project_name successfully', array('%project_name' => $project)));
  141. if (!empty($tasks)) {
  142. $context['results']['tasks'] += $tasks;
  143. }
  144. // This particular operation is now complete, even though the batch might
  145. // have other operations to perform.
  146. $context['finished'] = 1;
  147. }
  148. /**
  149. * Batch callback for when the authorized update batch is finished.
  150. *
  151. * This processes the results and stashes them into SESSION such that
  152. * authorize.php will render a report. Also responsible for putting the site
  153. * back online and clearing the update status cache after a successful update.
  154. */
  155. function update_authorize_update_batch_finished($success, $results) {
  156. foreach ($results['log'] as $project => $messages) {
  157. if (!empty($messages['#abort'])) {
  158. $success = FALSE;
  159. }
  160. }
  161. $offline = variable_get('maintenance_mode', FALSE);
  162. if ($success) {
  163. // Now that the update completed, we need to clear the cache of available
  164. // update data and recompute our status, so prevent show bogus results.
  165. _update_authorize_clear_update_status();
  166. // Take the site out of maintenance mode if it was previously that way.
  167. if ($offline && isset($_SESSION['maintenance_mode']) && $_SESSION['maintenance_mode'] == FALSE) {
  168. variable_set('maintenance_mode', FALSE);
  169. $page_message = array(
  170. 'message' => t('Update was completed successfully. Your site has been taken out of maintenance mode.'),
  171. 'type' => 'status',
  172. );
  173. }
  174. else {
  175. $page_message = array(
  176. 'message' => t('Update was completed successfully.'),
  177. 'type' => 'status',
  178. );
  179. }
  180. }
  181. elseif (!$offline) {
  182. $page_message = array(
  183. 'message' => t('Update failed! See the log below for more information.'),
  184. 'type' => 'error',
  185. );
  186. }
  187. else {
  188. $page_message = array(
  189. 'message' => t('Update failed! See the log below for more information. Your site is still in maintenance mode.'),
  190. 'type' => 'error',
  191. );
  192. }
  193. // Since we're doing an update of existing code, always add a task for
  194. // running update.php.
  195. $results['tasks'][] = t('Your modules have been downloaded and updated.');
  196. $results['tasks'][] = t('<a href="@update">Run database updates</a>', array('@update' => base_path() . 'update.php'));
  197. // Unset the variable since it is no longer needed.
  198. unset($_SESSION['maintenance_mode']);
  199. // Set all these values into the SESSION so authorize.php can display them.
  200. $_SESSION['authorize_results']['success'] = $success;
  201. $_SESSION['authorize_results']['page_message'] = $page_message;
  202. $_SESSION['authorize_results']['messages'] = $results['log'];
  203. $_SESSION['authorize_results']['tasks'] = $results['tasks'];
  204. $_SESSION['authorize_operation']['page_title'] = t('Update manager');
  205. }
  206. /**
  207. * Batch callback for when the authorized install batch is finished.
  208. *
  209. * This processes the results and stashes them into SESSION such that
  210. * authorize.php will render a report. Also responsible for putting the site
  211. * back online after a successful install if necessary.
  212. */
  213. function update_authorize_install_batch_finished($success, $results) {
  214. foreach ($results['log'] as $project => $messages) {
  215. if (!empty($messages['#abort'])) {
  216. $success = FALSE;
  217. }
  218. }
  219. $offline = variable_get('maintenance_mode', FALSE);
  220. if ($success) {
  221. // Take the site out of maintenance mode if it was previously that way.
  222. if ($offline && isset($_SESSION['maintenance_mode']) && $_SESSION['maintenance_mode'] == FALSE) {
  223. variable_set('maintenance_mode', FALSE);
  224. $page_message = array(
  225. 'message' => t('Installation was completed successfully. Your site has been taken out of maintenance mode.'),
  226. 'type' => 'status',
  227. );
  228. }
  229. else {
  230. $page_message = array(
  231. 'message' => t('Installation was completed successfully.'),
  232. 'type' => 'status',
  233. );
  234. }
  235. }
  236. elseif (!$success && !$offline) {
  237. $page_message = array(
  238. 'message' => t('Installation failed! See the log below for more information.'),
  239. 'type' => 'error',
  240. );
  241. }
  242. else {
  243. $page_message = array(
  244. 'message' => t('Installation failed! See the log below for more information. Your site is still in maintenance mode.'),
  245. 'type' => 'error',
  246. );
  247. }
  248. // Unset the variable since it is no longer needed.
  249. unset($_SESSION['maintenance_mode']);
  250. // Set all these values into the SESSION so authorize.php can display them.
  251. $_SESSION['authorize_results']['success'] = $success;
  252. $_SESSION['authorize_results']['page_message'] = $page_message;
  253. $_SESSION['authorize_results']['messages'] = $results['log'];
  254. $_SESSION['authorize_results']['tasks'] = $results['tasks'];
  255. $_SESSION['authorize_operation']['page_title'] = t('Update manager');
  256. }
  257. /**
  258. * Helper function to create a structure of log messages.
  259. *
  260. * @param array $project_results
  261. * @param string $message
  262. * @param bool $success
  263. */
  264. function _update_batch_create_message(&$project_results, $message, $success = TRUE) {
  265. $project_results[] = array('message' => $message, 'success' => $success);
  266. }
  267. /**
  268. * Private helper function to clear cached available update status data.
  269. *
  270. * Since this function is run at such a low bootstrap level, update.module is
  271. * not loaded. So, we can't just call _update_cache_clear(). However, the
  272. * database is bootstrapped, so we can do a query ourselves to clear out what
  273. * we want to clear.
  274. *
  275. * Note that we do not want to just truncate the table, since that would
  276. * remove items related to currently pending fetch attempts.
  277. *
  278. * @see update_authorize_update_batch_finished()
  279. * @see _update_cache_clear()
  280. */
  281. function _update_authorize_clear_update_status() {
  282. $query = db_delete('cache_update');
  283. $query->condition(
  284. db_or()
  285. ->condition('cid', 'update_project_%', 'LIKE')
  286. ->condition('cid', 'available_releases::%', 'LIKE')
  287. );
  288. $query->execute();
  289. }