summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAngie Byron2011-01-03 02:41:33 (GMT)
committer Angie Byron2011-01-03 02:41:33 (GMT)
commit81f6ecd362f279a0e1c468bff68c98ea2571bd2e (patch)
treea7ae0a7fe26f937cc6501adad014646edbb7088c
parentbfcdaf0607a563d69380f21602ae0d95c00f0402 (diff)
#686060 by Crell, David_Rothstein, dww, yoroy, carlos8f, et al: Fixed Explain that the Update manager only works if you have FTP or SSH access to your server
-rw-r--r--includes/authorize.inc7
-rw-r--r--modules/update/tests/update_test.module37
-rw-r--r--modules/update/update.manager.inc121
-rw-r--r--modules/update/update.test4
4 files changed, 164 insertions, 5 deletions
diff --git a/includes/authorize.inc b/includes/authorize.inc
index 5c4d1f0..4baf2f1 100644
--- a/includes/authorize.inc
+++ b/includes/authorize.inc
@@ -194,7 +194,12 @@ function authorize_filetransfer_form_validate($form, &$form_state) {
$filetransfer->connect();
}
catch (Exception $e) {
- form_set_error('connection_settings', $e->getMessage());
+ // The format of this error message is similar to that used on the
+ // database connection form in the installer.
+ form_set_error('connection_settings', t('Failed to connect to the server. The server reports the following message: !message For more help installing or updating code on your server, see the <a href="@handbook_url">handbook</a>.', array(
+ '!message' => '<p class="error">' . $e->getMessage() . '</p>',
+ '@handbook_url' => 'http://drupal.org/documentation/install/modules-themes',
+ )));
}
}
}
diff --git a/modules/update/tests/update_test.module b/modules/update/tests/update_test.module
index 9b8de5b..fb7d3ab 100644
--- a/modules/update/tests/update_test.module
+++ b/modules/update/tests/update_test.module
@@ -112,3 +112,40 @@ function update_test_archiver_info() {
),
);
}
+
+/**
+ * Implements hook_filetransfer_info().
+ */
+function update_test_filetransfer_info() {
+ // Define a mock file transfer method, to ensure that there will always be
+ // at least one method available in the user interface (regardless of the
+ // environment in which the update manager tests are run).
+ return array(
+ 'system_test' => array(
+ 'title' => t('Update Test FileTransfer'),
+ // This should be in an .inc file, but for testing purposes, it is OK to
+ // leave it in the main module file.
+ 'file' => 'update_test.module',
+ 'class' => 'UpdateTestFileTransfer',
+ 'weight' => -20,
+ ),
+ );
+}
+
+/**
+ * Mock FileTransfer object to test the settings form functionality.
+ */
+class UpdateTestFileTransfer {
+ public static function factory() {
+ return new UpdateTestFileTransfer;
+ }
+
+ public function getSettingsForm() {
+ $form = array();
+ $form['udpate_test_username'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Update Test Username'),
+ );
+ return $form;
+ }
+}
diff --git a/modules/update/update.manager.inc b/modules/update/update.manager.inc
index 0e69952..9f0fb8c 100644
--- a/modules/update/update.manager.inc
+++ b/modules/update/update.manager.inc
@@ -59,6 +59,10 @@
* The form array for selecting which projects to update.
*/
function update_manager_update_form($form, $form_state = array(), $context) {
+ if (!_update_manager_check_backends($form, 'update')) {
+ return $form;
+ }
+
$form['#theme'] = 'update_manager_update_form';
$available = update_get_available(TRUE);
@@ -354,6 +358,10 @@ function update_manager_download_batch_finished($success, $results) {
* file transfer credentials and attempt to complete the update.
*/
function update_manager_update_ready_form($form, &$form_state) {
+ if (!_update_manager_check_backends($form, 'update')) {
+ return $form;
+ }
+
$form['backup'] = array(
'#prefix' => '<strong>',
'#markup' => t('Back up your database and site before you continue. <a href="@backup_url">Learn how</a>.', array('@backup_url' => url('http://drupal.org/node/22281'))),
@@ -461,11 +469,18 @@ function update_manager_update_ready_form_submit($form, &$form_state) {
* The form array for selecting which project to install.
*/
function update_manager_install_form($form, &$form_state, $context) {
- $form = array();
+ if (!_update_manager_check_backends($form, 'install')) {
+ return $form;
+ }
$form['help_text'] = array(
'#prefix' => '<p>',
- '#markup' => t('To install a new module or theme, either enter the URL of an archive file you wish to install, or upload the archive file that you have downloaded. You can find <a href="@module_url">modules</a> and <a href="@theme_url">themes</a> at <a href="@drupal_org_url">http://drupal.org</a>.<br/>The following archive extensions are supported: %extensions.', array('@module_url' => 'http://drupal.org/project/modules', '@theme_url' => 'http://drupal.org/project/themes', '@drupal_org_url' => 'http://drupal.org', '%extensions' => archiver_get_extensions())),
+ '#markup' => t('You can find <a href="@module_url">modules</a> and <a href="@theme_url">themes</a> on <a href="@drupal_org_url">drupal.org</a>. The following file extensions are supported: %extensions.', array(
+ '@module_url' => 'http://drupal.org/project/modules',
+ '@theme_url' => 'http://drupal.org/project/themes',
+ '@drupal_org_url' => 'http://drupal.org',
+ '%extensions' => archiver_get_extensions(),
+ )),
'#suffix' => '</p>',
);
@@ -497,6 +512,73 @@ function update_manager_install_form($form, &$form_state, $context) {
}
/**
+ * Checks for file transfer backends and prepares a form fragment about them.
+ *
+ * @param array $form
+ * Reference to the form array we're building.
+ * @param string $operation
+ * The Update manager operation we're in the middle of. Can be either
+ * 'update' or 'install'. Use to provide operation-specific interface text.
+ *
+ * @return
+ * TRUE if the Update manager should continue to the next step in the
+ * workflow, or FALSE if we've hit a fatal configuration and must halt the
+ * workflow.
+ */
+function _update_manager_check_backends(&$form, $operation) {
+ // If file transfers will be performed locally, we do not need to display any
+ // warnings or notices to the user and should automatically continue the
+ // workflow, since we won't be using a FileTransfer backend that requires
+ // user input or a specific server configuration.
+ if (update_manager_local_transfers_allowed()) {
+ return TRUE;
+ }
+
+ // Otherwise, show the available backends.
+ $form['available_backends'] = array(
+ '#prefix' => '<p>',
+ '#suffix' => '</p>',
+ );
+
+ $available_backends = drupal_get_filetransfer_info();
+ if (empty($available_backends)) {
+ if ($operation == 'update') {
+ $form['available_backends']['#markup'] = t('Your server does not support updating modules and themes from this interface. Instead, update modules and themes by uploading the new versions directly to the server, as described in the <a href="@handbook_url">handbook</a>.', array('@handbook_url' => 'http://drupal.org/getting-started/install-contrib'));
+ }
+ else {
+ $form['available_backends']['#markup'] = t('Your server does not support installing modules and themes from this interface. Instead, install modules and themes by uploading them directly to the server, as described in the <a href="@handbook_url">handbook</a>.', array('@handbook_url' => 'http://drupal.org/getting-started/install-contrib'));
+ }
+ return FALSE;
+ }
+
+ $backend_names = array();
+ foreach ($available_backends as $backend) {
+ $backend_names[] = $backend['title'];
+ }
+ if ($operation == 'update') {
+ $form['available_backends']['#markup'] = format_plural(
+ count($available_backends),
+ 'Updating modules and themes requires <strong>@backends access</strong> to your server. See the <a href="@handbook_url">handbook</a> for other update methods.',
+ 'Updating modules and themes requires access to your server via one of the following methods: <strong>@backends</strong>. See the <a href="@handbook_url">handbook</a> for other update methods.',
+ array(
+ '@backends' => implode(', ', $backend_names),
+ '@handbook_url' => 'http://drupal.org/getting-started/install-contrib',
+ ));
+ }
+ else {
+ $form['available_backends']['#markup'] = format_plural(
+ count($available_backends),
+ 'Installing modules and themes requires <strong>@backends access</strong> to your server. See the <a href="@handbook_url">handbook</a> for other installation methods.',
+ 'Installing modules and themes requires access to your server via one of the following methods: <strong>@backends</strong>. See the <a href="@handbook_url">handbook</a> for other installation methods.',
+ array(
+ '@backends' => implode(', ', $backend_names),
+ '@handbook_url' => 'http://drupal.org/getting-started/install-contrib',
+ ));
+ }
+ return TRUE;
+}
+
+/**
* Validate the form for installing a new project via the update manager.
*/
function update_manager_install_form_validate($form, &$form_state) {
@@ -812,5 +894,40 @@ function update_manager_batch_project_get($project, $url, &$context) {
}
/**
+ * Determines if file transfers will be performed locally.
+ *
+ * If the server is configured such that webserver-created files have the same
+ * owner as the configuration directory (e.g. sites/default) where new code
+ * will eventually be installed, the Update manager can transfer files entirely
+ * locally, without changing their ownership (in other words, without prompting
+ * the user for FTP, SSH or other credentials).
+ *
+ * This server configuration is an inherent security weakness because it allows
+ * a malicious webserver process to append arbitrary PHP code and then execute
+ * it. However, it is supported here because it is a common configuration on
+ * shared hosting, and there is nothing Drupal can do to prevent it.
+ *
+ * @return
+ * TRUE if local file transfers are allowed on this server, or FALSE if not.
+ *
+ * @see update_manager_update_ready_form_submit()
+ * @see update_manager_install_form_submit()
+ * @see install_check_requirements()
+ */
+function update_manager_local_transfers_allowed() {
+ // Compare the owner of a webserver-created temporary file to the owner of
+ // the configuration directory to determine if local transfers will be
+ // allowed.
+ $temporary_file = drupal_tempnam('temporary://', 'update_');
+ $local_transfers_allowed = fileowner($temporary_file) === fileowner(conf_path());
+
+ // Clean up. If this fails, we can ignore it (since this is just a temporary
+ // file anyway).
+ @drupal_unlink($temporary_file);
+
+ return $local_transfers_allowed;
+}
+
+/**
* @} End of "defgroup update_manager_file".
*/
diff --git a/modules/update/update.test b/modules/update/update.test
index 8403715..a1252dc 100644
--- a/modules/update/update.test
+++ b/modules/update/update.test
@@ -622,9 +622,9 @@ class UpdateTestUploadCase extends UpdateTestHelper {
function testFileNameExtensionMerging() {
$this->drupalGet('admin/modules/install');
// Make sure the bogus extension supported by update_test.module is there.
- $this->assertPattern('/archive extensions are supported:.*update-test-extension/', t("Found 'update-test-extension' extension"));
+ $this->assertPattern('/file extensions are supported:.*update-test-extension/', t("Found 'update-test-extension' extension"));
// Make sure it didn't clobber the first option from core.
- $this->assertPattern('/archive extensions are supported:.*tar/', t("Found 'tar' extension"));
+ $this->assertPattern('/file extensions are supported:.*tar/', t("Found 'tar' extension"));
}
/**