diff --git a/core/includes/common.inc b/core/includes/common.inc index 0b1774696fb..240f644a154 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -7797,9 +7797,10 @@ function archiver_get_archiver($file) { * file system, for example to update modules that have newer releases, or to * install a new theme. * - * @return + * @return array * The Drupal Updater class registry. * + * @see Drupal\Core\Updater\Updater * @see hook_updater_info() * @see hook_updater_info_alter() */ diff --git a/core/includes/updater.inc b/core/includes/updater.inc deleted file mode 100644 index d4f99e9741a..00000000000 --- a/core/includes/updater.inc +++ /dev/null @@ -1,427 +0,0 @@ -source = $source; - $this->name = self::getProjectName($source); - $this->title = self::getProjectTitle($source); - } - - /** - * Return an Updater of the appropriate type depending on the source. - * - * If a directory is provided which contains a module, will return a - * ModuleUpdater. - * - * @param string $source - * Directory of a Drupal project. - * - * @return Updater - */ - public static function factory($source) { - if (is_dir($source)) { - $updater = self::getUpdaterFromDirectory($source); - } - else { - throw new UpdaterException(t('Unable to determine the type of the source directory.')); - } - return new $updater($source); - } - - /** - * Determine which Updater class can operate on the given directory. - * - * @param string $directory - * Extracted Drupal project. - * - * @return string - * The class name which can work with this project type. - */ - public static function getUpdaterFromDirectory($directory) { - // Gets a list of possible implementing classes. - $updaters = drupal_get_updaters(); - foreach ($updaters as $updater) { - $class = $updater['class']; - if (call_user_func(array($class, 'canUpdateDirectory'), $directory)) { - return $class; - } - } - throw new UpdaterException(t('Cannot determine the type of project.')); - } - - /** - * Figure out what the most important (or only) info file is in a directory. - * - * Since there is no enforcement of which info file is the project's "main" - * info file, this will get one with the same name as the directory, or the - * first one it finds. Not ideal, but needs a larger solution. - * - * @param string $directory - * Directory to search in. - * - * @return string - * Path to the info file. - */ - public static function findInfoFile($directory) { - $info_files = file_scan_directory($directory, '/.*\.info$/'); - if (!$info_files) { - return FALSE; - } - foreach ($info_files as $info_file) { - if (drupal_substr($info_file->filename, 0, -5) == drupal_basename($directory)) { - // Info file Has the same name as the directory, return it. - return $info_file->uri; - } - } - // Otherwise, return the first one. - $info_file = array_shift($info_files); - return $info_file->uri; - } - - /** - * Get the name of the project directory (basename). - * - * @todo: It would be nice, if projects contained an info file which could - * provide their canonical name. - * - * @param string $directory - * - * @return string - * The name of the project. - */ - public static function getProjectName($directory) { - return drupal_basename($directory); - } - - /** - * Return the project name from a Drupal info file. - * - * @param string $directory - * Directory to search for the info file. - * - * @return string - * The title of the project. - */ - public static function getProjectTitle($directory) { - $info_file = self::findInfoFile($directory); - $info = drupal_parse_info_file($info_file); - if (empty($info)) { - throw new UpdaterException(t('Unable to parse info file: %info_file.', array('%info_file' => $info_file))); - } - if (empty($info['name'])) { - throw new UpdaterException(t("The info file (%info_file) does not define a 'name' attribute.", array('%info_file' => $info_file))); - } - return $info['name']; - } - - /** - * Store the default parameters for the Updater. - * - * @param array $overrides - * An array of overrides for the default parameters. - * - * @return array - * An array of configuration parameters for an update or install operation. - */ - protected function getInstallArgs($overrides = array()) { - $args = array( - 'make_backup' => FALSE, - 'install_dir' => $this->getInstallDirectory(), - 'backup_dir' => $this->getBackupDir(), - ); - return array_merge($args, $overrides); - } - - /** - * Updates a Drupal project, returns a list of next actions. - * - * @param FileTransfer $filetransfer - * Object that is a child of FileTransfer. Used for moving files - * to the server. - * @param array $overrides - * An array of settings to override defaults; see self::getInstallArgs(). - * - * @return array - * An array of links which the user may need to complete the update - */ - public function update(&$filetransfer, $overrides = array()) { - try { - // Establish arguments with possible overrides. - $args = $this->getInstallArgs($overrides); - - // Take a Backup. - if ($args['make_backup']) { - $this->makeBackup($args['install_dir'], $args['backup_dir']); - } - - if (!$this->name) { - // This is bad, don't want to delete the install directory. - throw new UpdaterException(t('Fatal error in update, cowardly refusing to wipe out the install directory.')); - } - - // Make sure the installation parent directory exists and is writable. - $this->prepareInstallDirectory($filetransfer, $args['install_dir']); - - // Note: If the project is installed in sites/all, it will not be - // deleted. It will be installed in sites/default as that will override - // the sites/all reference and not break other sites which are using it. - if (is_dir($args['install_dir'] . '/' . $this->name)) { - // Remove the existing installed file. - $filetransfer->removeDirectory($args['install_dir'] . '/' . $this->name); - } - - // Copy the directory in place. - $filetransfer->copyDirectory($this->source, $args['install_dir']); - - // Make sure what we just installed is readable by the web server. - $this->makeWorldReadable($filetransfer, $args['install_dir'] . '/' . $this->name); - - // Run the updates. - // @TODO: decide if we want to implement this. - $this->postUpdate(); - - // For now, just return a list of links of things to do. - return $this->postUpdateTasks(); - } - catch (FileTransferException $e) { - throw new UpdaterFileTransferException(t('File Transfer failed, reason: !reason', array('!reason' => strtr($e->getMessage(), $e->arguments)))); - } - } - - /** - * Installs a Drupal project, returns a list of next actions. - * - * @param FileTransfer $filetransfer - * Object that is a child of FileTransfer. - * @param array $overrides - * An array of settings to override defaults; see self::getInstallArgs(). - * - * @return array - * An array of links which the user may need to complete the install. - */ - public function install(&$filetransfer, $overrides = array()) { - try { - // Establish arguments with possible overrides. - $args = $this->getInstallArgs($overrides); - - // Make sure the installation parent directory exists and is writable. - $this->prepareInstallDirectory($filetransfer, $args['install_dir']); - - // Copy the directory in place. - $filetransfer->copyDirectory($this->source, $args['install_dir']); - - // Make sure what we just installed is readable by the web server. - $this->makeWorldReadable($filetransfer, $args['install_dir'] . '/' . $this->name); - - // Potentially enable something? - // @TODO: decide if we want to implement this. - $this->postInstall(); - // For now, just return a list of links of things to do. - return $this->postInstallTasks(); - } - catch (FileTransferException $e) { - throw new UpdaterFileTransferException(t('File Transfer failed, reason: !reason', array('!reason' => strtr($e->getMessage(), $e->arguments)))); - } - } - - /** - * Make sure the installation parent directory exists and is writable. - * - * @param FileTransfer $filetransfer - * Object which is a child of FileTransfer. - * @param string $directory - * The installation directory to prepare. - */ - public function prepareInstallDirectory(&$filetransfer, $directory) { - // Make the parent dir writable if need be and create the dir. - if (!is_dir($directory)) { - $parent_dir = dirname($directory); - if (!is_writable($parent_dir)) { - @chmod($parent_dir, 0755); - // It is expected that this will fail if the directory is owned by the - // FTP user. If the FTP user == web server, it will succeed. - try { - $filetransfer->createDirectory($directory); - $this->makeWorldReadable($filetransfer, $directory); - } - catch (FileTransferException $e) { - // Probably still not writable. Try to chmod and do it again. - // @todo: Make a new exception class so we can catch it differently. - try { - $old_perms = substr(sprintf('%o', fileperms($parent_dir)), -4); - $filetransfer->chmod($parent_dir, 0755); - $filetransfer->createDirectory($directory); - $this->makeWorldReadable($filetransfer, $directory); - // Put the permissions back. - $filetransfer->chmod($parent_dir, intval($old_perms, 8)); - } - catch (FileTransferException $e) { - $message = t($e->getMessage(), $e->arguments); - $throw_message = t('Unable to create %directory due to the following: %reason', array('%directory' => $directory, '%reason' => $message)); - throw new UpdaterException($throw_message); - } - } - // Put the parent directory back. - @chmod($parent_dir, 0555); - } - } - } - - /** - * Ensure that a given directory is world readable. - * - * @param FileTransfer $filetransfer - * Object which is a child of FileTransfer. - * @param string $path - * The file path to make world readable. - * @param bool $recursive - * If the chmod should be applied recursively. - */ - public function makeWorldReadable(&$filetransfer, $path, $recursive = TRUE) { - if (!is_executable($path)) { - // Set it to read + execute. - $new_perms = substr(sprintf('%o', fileperms($path)), -4, -1) . "5"; - $filetransfer->chmod($path, intval($new_perms, 8), $recursive); - } - } - - /** - * Perform a backup. - * - * @todo Not implemented. - */ - public function makeBackup(&$filetransfer, $from, $to) { - } - - /** - * Return the full path to a directory where backups should be written. - */ - public function getBackupDir() { - return file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(); - } - - /** - * Perform actions after new code is updated. - */ - public function postUpdate() { - } - - /** - * Perform actions after installation. - */ - public function postInstall() { - } - - /** - * Return an array of links to pages that should be visited post operation. - * - * @return array - * Links which provide actions to take after the install is finished. - */ - public function postInstallTasks() { - return array(); - } - - /** - * Return an array of links to pages that should be visited post operation. - * - * @return array - * Links which provide actions to take after the update is finished. - */ - public function postUpdateTasks() { - return array(); - } -} - -/** - * Exception class for the Updater class hierarchy. - * - * This is identical to the base Exception class, we just give it a more - * specific name so that call sites that want to tell the difference can - * specifically catch these exceptions and treat them differently. - */ -class UpdaterException extends Exception { -} - -/** - * Child class of UpdaterException that indicates a FileTransfer exception. - * - * We have to catch FileTransfer exceptions and wrap those in t(), since - * FileTransfer is so low-level that it doesn't use any Drupal APIs and none - * of the strings are translated. - */ -class UpdaterFileTransferException extends UpdaterException { -} diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index eac29fefcf2..e1d5298eab4 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -4042,9 +4042,9 @@ function hook_batch_alter(&$batch) { /** * Provide information on Updaters (classes that can update Drupal). * - * An Updater is a class that knows how to update various parts of the Drupal - * file system, for example to update modules that have newer releases, or to - * install a new theme. + * Drupal\Core\Updater\Updater is a class that knows how to update various parts + * of the Drupal file system, for example to update modules that have newer + * releases, or to install a new theme. * * @return * An associative array of information about the updater(s) being provided. @@ -4066,12 +4066,12 @@ function hook_batch_alter(&$batch) { function hook_updater_info() { return array( 'module' => array( - 'class' => 'ModuleUpdater', + 'class' => 'Drupal\Core\Updater\Module', 'name' => t('Update modules'), 'weight' => 0, ), 'theme' => array( - 'class' => 'ThemeUpdater', + 'class' => 'Drupal\Core\Updater\Theme', 'name' => t('Update themes'), 'weight' => 0, ), diff --git a/core/modules/system/system.info b/core/modules/system/system.info index c394849e73b..d95d5611f10 100644 --- a/core/modules/system/system.info +++ b/core/modules/system/system.info @@ -7,7 +7,6 @@ files[] = system.archiver.inc files[] = system.mail.inc files[] = system.queue.inc files[] = system.tar.inc -files[] = system.updater.inc files[] = system.test required = TRUE configure = admin/config/system diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 14e73f5a45a..469f996655d 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1862,12 +1862,12 @@ function system_authorized_batch_process() { function system_updater_info() { return array( 'module' => array( - 'class' => 'ModuleUpdater', + 'class' => 'Drupal\Core\Updater\Module', 'name' => t('Update modules'), 'weight' => 0, ), 'theme' => array( - 'class' => 'ThemeUpdater', + 'class' => 'Drupal\Core\Updater\Theme', 'name' => t('Update themes'), 'weight' => 0, ), diff --git a/core/modules/system/system.updater.inc b/core/modules/system/system.updater.inc deleted file mode 100644 index 84c1752d3bf..00000000000 --- a/core/modules/system/system.updater.inc +++ /dev/null @@ -1,146 +0,0 @@ -name)) { - $relative_path = dirname($relative_path); - } - else { - $relative_path = 'sites/all/modules'; - } - return DRUPAL_ROOT . '/' . $relative_path; - } - - public function isInstalled() { - return (bool) drupal_get_path('module', $this->name); - } - - public static function canUpdateDirectory($directory) { - if (file_scan_directory($directory, '/.*\.module$/')) { - return TRUE; - } - return FALSE; - } - - public static function canUpdate($project_name) { - return (bool) drupal_get_path('module', $project_name); - } - - /** - * Return available database schema updates one a new version is installed. - */ - public function getSchemaUpdates() { - require_once DRUPAL_ROOT . '/core/includes/install.inc'; - require_once DRUPAL_ROOT . '/core/includes/update.inc'; - - if (_update_get_project_type($project) != 'module') { - return array(); - } - module_load_include('install', $project); - - if (!$updates = drupal_get_schema_versions($project)) { - return array(); - } - $updates_to_run = array(); - $modules_with_updates = update_get_update_list(); - if ($updates = $modules_with_updates[$project]) { - if ($updates['start']) { - return $updates['pending']; - } - } - return array(); - } - - public function postInstallTasks() { - return array( - l(t('Enable newly added modules'), 'admin/modules'), - l(t('Administration pages'), 'admin'), - ); - } - - public function postUpdateTasks() { - // We don't want to check for DB updates here, we do that once for all - // updated modules on the landing page. - } - -} - -/** - * Class for updating themes using FileTransfer classes via authorize.php. - */ -class ThemeUpdater extends Updater implements DrupalUpdaterInterface { - - /** - * Return the directory where a theme should be installed. - * - * If the theme is already installed, drupal_get_path() will return - * a valid path and we should install it there (although we need to use an - * absolute path, so we prepend DRUPAL_ROOT). If we're installing a new - * theme, we always want it to go into sites/all/themes, since that's - * where all the documentation recommends users install their themes, and - * there's no way that can conflict on a multi-site installation, since - * the Update manager won't let you install a new theme if it's already - * found on your system, and if there was a copy in sites/all, we'd see it. - */ - public function getInstallDirectory() { - if ($relative_path = drupal_get_path('theme', $this->name)) { - $relative_path = dirname($relative_path); - } - else { - $relative_path = 'sites/all/themes'; - } - return DRUPAL_ROOT . '/' . $relative_path; - } - - public function isInstalled() { - return (bool) drupal_get_path('theme', $this->name); - } - - static function canUpdateDirectory($directory) { - // This is a lousy test, but don't know how else to confirm it is a theme. - if (file_scan_directory($directory, '/.*\.module$/')) { - return FALSE; - } - return TRUE; - } - - public static function canUpdate($project_name) { - return (bool) drupal_get_path('theme', $project_name); - } - - public function postInstall() { - // Update the system table. - clearstatcache(); - system_rebuild_theme_data(); - - } - - public function postInstallTasks() { - return array( - l(t('Enable newly added themes'), 'admin/appearance'), - l(t('Administration pages'), 'admin'), - ); - } -} diff --git a/core/modules/update/update.authorize.inc b/core/modules/update/update.authorize.inc index 35dde0eb4fc..48dfd353f21 100644 --- a/core/modules/update/update.authorize.inc +++ b/core/modules/update/update.authorize.inc @@ -9,6 +9,8 @@ * the rest of the update module code. */ +use Drupal\Core\Updater\UpdaterException; + /** * Callback invoked by authorize.php to update existing projects. * @@ -19,7 +21,8 @@ * A nested array of projects to install into the live webroot, keyed by * project name. Each subarray contains the following keys: * - 'project': The canonical project short name. - * - 'updater_name': The name of the Updater class to use for this project. + * - 'updater_name': The name of the Drupal\Core\Updater\Updater class to use + * for this project. * - 'local_url': The locally installed location of new code to update with. */ function update_authorize_run_update($filetransfer, $projects) { @@ -58,7 +61,8 @@ function update_authorize_run_update($filetransfer, $projects) { * @param string $project * The canonical project short name (e.g. {system}.name). * @param string $updater_name - * The name of the Updater class to use for installing this project. + * The name of the Drupal\Core\Updater\Updater class to use for installing + * this project. * @param string $local_url * The URL to the locally installed temp directory where the project has * already been downloaded and extracted into. @@ -95,7 +99,8 @@ function update_authorize_run_install($filetransfer, $project, $updater_name, $l * @param string $project * The canonical short name of the project being installed. * @param string $updater_name - * The name of the Updater class to use for installing this project. + * The name of the Drupal\Core\Updater\Updater class to use for installing + * this project. * @param string $local_url * The URL to the locally installed temp directory where the project has * already been downloaded and extracted into. diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index f56dd0b1585..82b48f3a68b 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -35,6 +35,8 @@ * into the live web root. */ +use Drupal\Core\Updater\Updater; + /** * @defgroup update_manager_update Update manager: update * @{ @@ -393,9 +395,10 @@ function update_manager_update_ready_form($form, &$form_state) { * * If the site administrator requested that the site is put offline during the * update, do so now. Otherwise, pull information about all the required - * updates out of the SESSION, figure out what Updater class is needed for - * each one, generate an array of update operations to perform, and hand it - * all off to system_authorized_init(), then redirect to authorize.php. + * updates out of the SESSION, figure out what Drupal\Core\Updater\Updater class + * is needed for each one, generate an array of update operations to perform, + * and hand it all off to system_authorized_init(), then redirect to + * authorize.php. * * @see update_authorize_run_update() * @see system_authorized_init() @@ -600,10 +603,10 @@ function update_manager_install_form_validate($form, &$form_state) { * Either downloads the file specified in the URL to a temporary cache, or * uploads the file attached to the form, then attempts to extract the archive * into a temporary location and verify it. Instantiate the appropriate - * Updater class for this project and make sure it is not already installed in - * the live webroot. If everything is successful, setup an operation to run - * via authorize.php which will copy the extracted files from the temporary - * location into the live site. + * Drupal\Core\Updater\Updater class for this project and make sure it is not + * already installed in the live webroot. If everything is successful, setup an + * operation to run via authorize.php which will copy the extracted files from + * the temporary location into the live site. * * @see update_authorize_run_install() * @see system_authorized_init()