summaryrefslogtreecommitdiff
path: root/modules/update
diff options
context:
space:
mode:
Diffstat (limited to 'modules/update')
-rw-r--r--modules/update/update.fetch.inc24
-rw-r--r--modules/update/update.module13
-rw-r--r--modules/update/update.test83
3 files changed, 108 insertions, 12 deletions
diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc
index 7ac0dbefb..ee5d77b16 100644
--- a/modules/update/update.fetch.inc
+++ b/modules/update/update.fetch.inc
@@ -237,12 +237,22 @@ function _update_create_fetch_task($project) {
if (empty($fetch_tasks[$cid])) {
$queue = DrupalQueue::get('update_fetch_tasks');
$queue->createItem($project);
- db_insert('cache_update')
- ->fields(array(
- 'cid' => $cid,
- 'created' => REQUEST_TIME,
- ))
- ->execute();
+ // Due to race conditions, it is possible that another process already
+ // inserted a row into the {cache_update} table and the following query will
+ // throw an exception.
+ // @todo: Remove the need for the manual check by relying on a queue that
+ // enforces unique items.
+ try {
+ db_insert('cache_update')
+ ->fields(array(
+ 'cid' => $cid,
+ 'created' => REQUEST_TIME,
+ ))
+ ->execute();
+ }
+ catch (Exception $e) {
+ // The exception can be ignored safely.
+ }
$fetch_tasks[$cid] = REQUEST_TIME;
}
}
@@ -271,7 +281,7 @@ function _update_build_fetch_url($project, $site_key = '') {
// in the first place, and if this is not a disabled module or theme. We do
// not want to record usage statistics for disabled code.
if (!empty($site_key) && (strpos($project['project_type'], 'disabled') === FALSE)) {
- $url .= (strpos($url, '?') === TRUE) ? '&' : '?';
+ $url .= (strpos($url, '?') !== FALSE) ? '&' : '?';
$url .= 'site_key=';
$url .= rawurlencode($site_key);
if (!empty($project['info']['version'])) {
diff --git a/modules/update/update.module b/modules/update/update.module
index 293a53d90..bf8b06848 100644
--- a/modules/update/update.module
+++ b/modules/update/update.module
@@ -81,7 +81,7 @@ function update_help($path, $arg) {
case 'admin/help#update':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
- $output .= '<p>' . t("The Update manager module periodically checks for new versions of your site's software (including contributed modules and themes), and alerts administrators to available updates. In order to provide update information, anonymous usage statistics are sent to Drupal.org. If desired, you may disable the Update manager module from the <a href='@modules'>Module administration page</a>. For more information, see the online handbook entry for <a href='@update'>Update manager module</a>.", array('@update' => 'http://drupal.org/handbook/modules/update', '@modules' => url('admin/modules'))) . '</p>';
+ $output .= '<p>' . t("The Update manager module periodically checks for new versions of your site's software (including contributed modules and themes), and alerts administrators to available updates. In order to provide update information, anonymous usage statistics are sent to Drupal.org. If desired, you may disable the Update manager module from the <a href='@modules'>Module administration page</a>. For more information, see the online handbook entry for <a href='@update'>Update manager module</a>.", array('@update' => 'http://drupal.org/documentation/modules/update', '@modules' => url('admin/modules'))) . '</p>';
// Only explain the Update manager if it has not been disabled.
if (update_manager_access()) {
$output .= '<p>' . t('The Update manager also allows administrators to update and install modules and themes through the administration interface.') . '</p>';
@@ -93,7 +93,7 @@ function update_help($path, $arg) {
// Only explain the Update manager if it has not been disabled.
if (update_manager_access()) {
$output .= '<dt>' . t('Performing updates through the user interface') . '</dt>';
- $output .= '<dd>' . t('The Update manager module allows administrators to perform updates directly through the administration interface. At the top of the <a href="@modules_page">modules</a> and <a href="@themes_page">themes</a> pages you will see a link to update to new releases. This will direct you to the <a href="@update-page">update page</a> where you see a listing of all the missing updates and confirm which ones you want to upgrade. From there, you are prompted for your FTP/SSH password, which then transfers the files into your Drupal installation, overwriting your old files. More detailed instructions can be found in the <a href="@update">online handbook</a>.', array('@modules_page' => url('admin/modules'), '@themes_page' => url('admin/appearance'), '@update-page' => url('admin/reports/updates/update'), '@update' => 'http://drupal.org/handbook/modules/update')) . '</dd>';
+ $output .= '<dd>' . t('The Update manager module allows administrators to perform updates directly through the administration interface. At the top of the <a href="@modules_page">modules</a> and <a href="@themes_page">themes</a> pages you will see a link to update to new releases. This will direct you to the <a href="@update-page">update page</a> where you see a listing of all the missing updates and confirm which ones you want to upgrade. From there, you are prompted for your FTP/SSH password, which then transfers the files into your Drupal installation, overwriting your old files. More detailed instructions can be found in the <a href="@update">online handbook</a>.', array('@modules_page' => url('admin/modules'), '@themes_page' => url('admin/appearance'), '@update-page' => url('admin/reports/updates/update'), '@update' => 'http://drupal.org/documentation/modules/update')) . '</dd>';
$output .= '<dt>' . t('Installing new modules and themes through the user interface') . '</dt>';
$output .= '<dd>' . t('You can also install new modules and themes in the same fashion, through the <a href="@install">install page</a>, or by clicking the <em>Install new module/theme</em> link at the top of the <a href="@modules_page">modules</a> and <a href="@themes_page">themes</a> pages. In this case, you are prompted to provide either the URL to the download, or to upload a packaged release file from your local computer.', array('@modules_page' => url('admin/modules'), '@themes_page' => url('admin/appearance'), '@install' => url('admin/reports/updates/install'))) . '</dd>';
}
@@ -809,14 +809,19 @@ function _update_get_cache_multiple($cid_prefix) {
*
* @param $cid
* Optional cache ID of the record to clear from the private update module
- * cache. If empty, all records will be cleared from the table.
+ * cache. If empty, all records will be cleared from the table except
+ * fetch tasks.
* @param $wildcard
* If $wildcard is TRUE, cache IDs starting with $cid are deleted in
* addition to the exact cache ID specified by $cid.
*/
function _update_cache_clear($cid = NULL, $wildcard = FALSE) {
if (empty($cid)) {
- db_truncate('cache_update')->execute();
+ db_delete('cache_update')
+ // Clear everything except fetch task information because these are used
+ // to ensure that the fetch task queue items are not added multiple times.
+ ->condition('cid', 'fetch_task::%', 'NOT LIKE')
+ ->execute();
}
else {
$query = db_delete('cache_update');
diff --git a/modules/update/update.test b/modules/update/update.test
index c0f471a0a..8daa82155 100644
--- a/modules/update/update.test
+++ b/modules/update/update.test
@@ -225,6 +225,33 @@ class UpdateCoreTestCase extends UpdateTestHelper {
$this->assertUniqueText(t('Failed to get available update data for one project.'));
}
+ /**
+ * Tests that exactly one fetch task per project is created and not more.
+ */
+ function testFetchTasks() {
+ $projecta = array(
+ 'name' => 'aaa_update_test',
+ );
+ $projectb = array(
+ 'name' => 'bbb_update_test',
+ );
+ $queue = DrupalQueue::get('update_fetch_tasks');
+ $this->assertEqual($queue->numberOfItems(), 0, 'Queue is empty');
+ update_create_fetch_task($projecta);
+ $this->assertEqual($queue->numberOfItems(), 1, 'Queue contains one item');
+ update_create_fetch_task($projectb);
+ $this->assertEqual($queue->numberOfItems(), 2, 'Queue contains two items');
+ // Try to add project a again.
+ update_create_fetch_task($projecta);
+ $this->assertEqual($queue->numberOfItems(), 2, 'Queue still contains two items');
+
+ // Clear cache and try again.
+ _update_cache_clear();
+ drupal_static_reset('_update_create_fetch_task');
+ update_create_fetch_task($projecta);
+ $this->assertEqual($queue->numberOfItems(), 2, 'Queue contains two items');
+ }
+
protected function setSystemInfo7_0() {
$setting = array(
'#all' => array(
@@ -649,7 +676,7 @@ class UpdateTestUploadCase extends UpdateTestHelper {
* Ensure that archiver extensions are properly merged in the UI.
*/
function testFileNameExtensionMerging() {
- $this->drupalGet('admin/modules/install');
+ $this->drupalGet('admin/modules/install');
// Make sure the bogus extension supported by update_test.module is there.
$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.
@@ -697,3 +724,57 @@ class UpdateTestUploadCase extends UpdateTestHelper {
}
}
+
+class UpdateCoreUnitTestCase extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => "Unit tests",
+ 'description' => 'Test update funcionality unrelated to the database.',
+ 'group' => 'Update',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('update');
+ module_load_include('inc', 'update', 'update.fetch');
+ }
+
+ /**
+ * Tests _update_build_fetch_url according to issue 1481156
+ */
+ function testUpdateBuildFetchUrl() {
+ //first test that we didn't break the trivial case
+ $project['name'] = 'update_test';
+ $project['project_type'] = '';
+ $project['info']['version'] = '';
+ $project['info']['project status url'] = 'http://www.example.com';
+ $site_key = '';
+ $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
+ $url = _update_build_fetch_url($project, $site_key);
+ $this->assertEqual($url, $expected, "'$url' when no site_key provided should be '$expected'.");
+
+ //For disabled projects it shouldn't add the site key either.
+ $site_key = 'site_key';
+ $project['project_type'] = 'disabled';
+ $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
+ $url = _update_build_fetch_url($project, $site_key);
+ $this->assertEqual($url, $expected, "'$url' should be '$expected' for disabled projects.");
+
+ //for enabled projects, adding the site key
+ $project['project_type'] = '';
+ $expected = 'http://www.example.com/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
+ $expected .= '?site_key=site_key';
+ $url = _update_build_fetch_url($project, $site_key);
+ $this->assertEqual($url, $expected, "When site_key provided, '$url' should be '$expected'.");
+
+ // http://drupal.org/node/1481156 test incorrect logic when url contains
+ // a question mark.
+ $project['info']['project status url'] = 'http://www.example.com/?project=';
+ $expected = 'http://www.example.com/?project=/' . $project['name'] . '/' . DRUPAL_CORE_COMPATIBILITY;
+ $expected .= '&site_key=site_key';
+ $url = _update_build_fetch_url($project, $site_key);
+ $this->assertEqual($url, $expected, "When ? is present, '$url' should be '$expected'.");
+
+ }
+} \ No newline at end of file