From 174d1ce37b414301b367f7286f000ccdb6f03e66 Mon Sep 17 00:00:00 2001 From: Tim Steiner <tsteiner2@unl.edu> Date: Fri, 3 Aug 2012 09:59:36 -0500 Subject: [PATCH] [gh-441] Update drupal core to 7.15 --- CHANGELOG.txt | 52 ++- COPYRIGHT.txt | 25 +- MAINTAINERS.txt | 6 +- UPGRADE.txt | 2 +- authorize.php | 1 - includes/bootstrap.inc | 49 ++- includes/common.inc | 107 ++++-- includes/database/database.inc | 9 +- includes/database/mysql/database.inc | 4 +- includes/database/mysql/query.inc | 4 +- includes/database/mysql/schema.inc | 4 +- includes/database/pgsql/database.inc | 4 +- includes/database/pgsql/select.inc | 4 +- includes/database/prefetch.inc | 4 +- includes/database/query.inc | 4 +- includes/database/select.inc | 4 +- includes/database/sqlite/database.inc | 4 +- includes/database/sqlite/query.inc | 4 +- includes/database/sqlite/select.inc | 4 +- includes/entity.inc | 23 +- includes/errors.inc | 6 +- includes/form.inc | 66 ++-- includes/graph.inc | 8 +- includes/locale.inc | 25 +- includes/module.inc | 27 ++ includes/theme.inc | 185 +++++++--- includes/update.inc | 2 +- misc/autocomplete.js | 6 +- misc/states.js | 3 +- misc/tableselect.js | 3 +- modules/aggregator/aggregator.info | 6 +- modules/aggregator/tests/aggregator_test.info | 6 +- modules/block/block.info | 6 +- modules/block/block.install | 11 +- modules/block/block.test | 4 +- modules/block/tests/block_test.info | 6 +- .../block_test_theme/block_test_theme.info | 6 +- modules/blog/blog.info | 6 +- modules/book/book.info | 6 +- modules/book/book.module | 15 +- modules/book/book.pages.inc | 5 +- modules/book/book.test | 35 +- modules/color/color.info | 6 +- modules/color/color.install | 9 + modules/comment/comment.info | 6 +- modules/comment/comment.install | 20 +- modules/comment/comment.module | 1 + modules/comment/comment.pages.inc | 8 +- modules/comment/comment.test | 25 ++ modules/contact/contact.info | 6 +- modules/contact/contact.install | 2 +- modules/contextual/contextual.info | 6 +- modules/dashboard/dashboard.info | 6 +- modules/dblog/dblog.info | 6 +- modules/dblog/dblog.install | 2 +- modules/dblog/dblog.test | 4 +- modules/field/field.api.php | 84 ++--- modules/field/field.attach.inc | 4 +- modules/field/field.form.inc | 193 +++++----- modules/field/field.info | 6 +- modules/field/field.info.inc | 5 +- modules/field/field.install | 4 +- modules/field/field.module | 82 ++--- .../field_sql_storage/field_sql_storage.info | 6 +- .../field_sql_storage.install | 2 +- .../field_sql_storage.module | 7 +- modules/field/modules/list/list.info | 6 +- modules/field/modules/list/list.install | 11 +- .../field/modules/list/tests/list_test.info | 6 +- modules/field/modules/number/number.info | 6 +- modules/field/modules/options/options.info | 6 +- modules/field/modules/text/text.info | 6 +- modules/field/tests/field.test | 14 +- modules/field/tests/field_test.info | 6 +- modules/field/tests/field_test.module | 11 + modules/field_ui/field_ui.admin.inc | 5 +- modules/field_ui/field_ui.api.php | 2 +- modules/field_ui/field_ui.info | 6 +- modules/field_ui/field_ui.js | 2 +- modules/field_ui/field_ui.test | 2 +- modules/file/file.field.inc | 8 +- modules/file/file.info | 6 +- modules/file/file.js | 2 +- modules/file/tests/file.test | 2 +- modules/file/tests/file_module_test.info | 6 +- modules/filter/filter.info | 6 +- modules/filter/filter.install | 2 +- modules/filter/filter.test | 25 +- modules/forum/forum-icon.tpl.php | 4 +- modules/forum/forum-list.tpl.php | 45 +-- modules/forum/forum-rtl.css | 4 + modules/forum/forum-submitted.tpl.php | 12 +- modules/forum/forum-topic-list.tpl.php | 44 +-- modules/forum/forum.admin.inc | 77 +++- modules/forum/forum.css | 4 + modules/forum/forum.info | 6 +- modules/forum/forum.install | 32 +- modules/forum/forum.module | 183 +++++++--- modules/forum/forum.pages.inc | 13 +- modules/forum/forum.test | 98 +++-- modules/forum/forums.tpl.php | 14 +- modules/help/help.info | 6 +- modules/image/image.admin.inc | 2 +- modules/image/image.field.inc | 4 +- modules/image/image.info | 6 +- modules/image/image.install | 9 + modules/image/tests/image_module_test.info | 6 +- modules/locale/locale.datepicker.js | 138 +++---- modules/locale/locale.info | 6 +- modules/locale/locale.install | 18 +- modules/locale/locale.module | 32 +- modules/locale/locale.test | 53 ++- modules/locale/tests/locale_test.info | 6 +- modules/menu/menu.info | 6 +- modules/menu/menu.install | 4 +- modules/node/content_types.inc | 2 +- modules/node/node.admin.inc | 9 +- modules/node/node.info | 6 +- modules/node/node.install | 13 +- modules/node/node.module | 26 +- modules/node/node.pages.inc | 9 +- modules/node/node.test | 108 +++++- modules/node/node.tokens.inc | 3 +- modules/node/tests/node_access_test.info | 6 +- modules/node/tests/node_test.info | 6 +- modules/node/tests/node_test_exception.info | 6 +- modules/openid/openid.info | 6 +- modules/openid/openid.install | 2 +- modules/openid/tests/openid_test.info | 6 +- modules/overlay/overlay.info | 6 +- modules/path/path.info | 6 +- modules/path/path.module | 35 +- modules/php/php.info | 6 +- modules/php/php.test | 4 +- modules/poll/poll.info | 6 +- modules/poll/poll.install | 9 + modules/profile/profile.info | 6 +- modules/rdf/rdf.info | 6 +- modules/rdf/tests/rdf_test.info | 6 +- modules/search/search.info | 6 +- .../search/tests/search_embedded_form.info | 6 +- modules/search/tests/search_extra_type.info | 6 +- modules/shortcut/shortcut.info | 6 +- modules/simpletest/drupal_web_test_case.php | 204 ++++++++--- modules/simpletest/files/README.txt | 8 +- modules/simpletest/simpletest.info | 6 +- modules/simpletest/simpletest.js | 71 ++-- modules/simpletest/tests/actions.test | 2 +- .../simpletest/tests/actions_loop_test.info | 6 +- modules/simpletest/tests/ajax_forms_test.info | 6 +- modules/simpletest/tests/ajax_test.info | 6 +- modules/simpletest/tests/batch.test | 2 - modules/simpletest/tests/batch_test.info | 6 +- modules/simpletest/tests/common_test.info | 6 +- .../tests/common_test_cron_helper.info | 6 +- modules/simpletest/tests/database_test.info | 6 +- ...drupal_system_listing_compatible_test.info | 6 +- ...upal_system_listing_incompatible_test.info | 6 +- .../simpletest/tests/entity_cache_test.info | 6 +- .../tests/entity_cache_test_dependency.info | 6 +- .../tests/entity_crud_hook_test.info | 6 +- modules/simpletest/tests/entity_query.test | 41 ++- .../tests/entity_query_access_test.info | 12 + .../tests/entity_query_access_test.module | 54 +++ modules/simpletest/tests/error_test.info | 6 +- modules/simpletest/tests/file_test.info | 6 +- modules/simpletest/tests/filter_test.info | 6 +- modules/simpletest/tests/form.test | 51 ++- modules/simpletest/tests/form_test.info | 6 +- modules/simpletest/tests/form_test.module | 41 +++ modules/simpletest/tests/image_test.info | 6 +- modules/simpletest/tests/menu.test | 4 +- modules/simpletest/tests/menu_test.info | 6 +- modules/simpletest/tests/module_test.info | 6 +- modules/simpletest/tests/path_test.info | 6 +- .../simpletest/tests/requirements1_test.info | 6 +- .../simpletest/tests/requirements2_test.info | 6 +- modules/simpletest/tests/session_test.info | 6 +- .../tests/system_dependencies_test.info | 6 +- ...atible_core_version_dependencies_test.info | 6 +- ...system_incompatible_core_version_test.info | 6 +- ...ible_module_version_dependencies_test.info | 6 +- ...stem_incompatible_module_version_test.info | 6 +- modules/simpletest/tests/system_test.info | 6 +- modules/simpletest/tests/taxonomy_test.info | 6 +- modules/simpletest/tests/theme.test | 31 ++ modules/simpletest/tests/theme_test.info | 6 +- modules/simpletest/tests/theme_test.module | 2 + .../themes/test_basetheme/test_basetheme.info | 13 + .../themes/test_subtheme/test_subtheme.info | 13 + .../tests/themes/test_theme/test_theme.info | 8 +- .../simpletest/tests/update_script_test.info | 6 +- modules/simpletest/tests/update_test_1.info | 6 +- modules/simpletest/tests/update_test_2.info | 6 +- modules/simpletest/tests/update_test_3.info | 6 +- modules/simpletest/tests/upgrade/upgrade.test | 134 ++----- modules/simpletest/tests/url_alter_test.info | 6 +- modules/simpletest/tests/xmlrpc_test.info | 6 +- modules/statistics/statistics.admin.inc | 52 ++- modules/statistics/statistics.info | 6 +- modules/statistics/statistics.install | 4 +- modules/statistics/statistics.module | 59 +-- modules/statistics/statistics.pages.inc | 16 +- modules/statistics/statistics.test | 28 +- modules/syslog/syslog.info | 6 +- modules/system/image.gd.inc | 4 +- modules/system/system.admin.inc | 2 +- modules/system/system.api.php | 94 +++-- modules/system/system.base.css | 1 + modules/system/system.info | 6 +- modules/system/system.install | 31 +- modules/system/system.module | 45 +-- modules/system/theme.api.php | 24 +- modules/taxonomy/taxonomy.admin.inc | 3 +- modules/taxonomy/taxonomy.info | 6 +- modules/taxonomy/taxonomy.install | 120 +++++-- modules/taxonomy/taxonomy.module | 39 +- modules/taxonomy/taxonomy.test | 336 +++++++++++------- modules/toolbar/toolbar.info | 6 +- modules/tracker/tracker.info | 6 +- modules/tracker/tracker.install | 2 +- .../translation/tests/translation_test.info | 6 +- modules/translation/translation.info | 6 +- modules/translation/translation.module | 20 +- modules/translation/translation.pages.inc | 2 +- modules/translation/translation.test | 2 +- modules/trigger/tests/trigger_test.info | 6 +- modules/trigger/trigger.admin.inc | 2 +- modules/trigger/trigger.info | 6 +- modules/trigger/trigger.install | 9 + modules/trigger/trigger.module | 24 +- modules/trigger/trigger.test | 29 ++ modules/update/tests/aaa_update_test.info | 6 +- modules/update/tests/bbb_update_test.info | 6 +- modules/update/tests/ccc_update_test.info | 6 +- .../update_test_basetheme.info | 6 +- .../update_test_subtheme.info | 6 +- modules/update/tests/update_test.info | 6 +- modules/update/tests/update_test.module | 64 ++-- modules/update/update-rtl.css | 4 + modules/update/update.api.php | 30 +- modules/update/update.authorize.inc | 83 +++-- modules/update/update.compare.inc | 185 ++++++---- modules/update/update.css | 4 + modules/update/update.fetch.inc | 61 ++-- modules/update/update.info | 6 +- modules/update/update.install | 40 +-- modules/update/update.manager.inc | 184 +++++----- modules/update/update.module | 214 ++++++----- modules/update/update.report.inc | 5 +- modules/update/update.settings.inc | 26 +- modules/update/update.test | 122 ++++--- modules/user/tests/user_form_test.info | 6 +- modules/user/user.info | 6 +- modules/user/user.install | 20 +- modules/user/user.module | 14 +- modules/user/user.pages.inc | 8 +- modules/user/user.test | 62 +++- profiles/minimal/minimal.info | 6 +- profiles/minimal/minimal.install | 2 +- profiles/standard/standard.info | 6 +- ...drupal_system_listing_compatible_test.info | 6 +- ...upal_system_listing_incompatible_test.info | 6 +- profiles/testing/testing.info | 6 +- sites/default/default.settings.php | 2 +- themes/bartik/bartik.info | 6 +- themes/bartik/css/style.css | 2 +- themes/bartik/template.php | 2 +- themes/garland/garland.info | 6 +- themes/seven/seven.info | 6 +- themes/stark/stark.info | 6 +- 271 files changed, 3712 insertions(+), 2018 deletions(-) create mode 100644 modules/simpletest/tests/entity_query_access_test.info create mode 100644 modules/simpletest/tests/entity_query_access_test.module create mode 100644 modules/simpletest/tests/themes/test_basetheme/test_basetheme.info create mode 100644 modules/simpletest/tests/themes/test_subtheme/test_subtheme.info diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 231618d0c..09662cf88 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,55 @@ +Drupal 7.15, 2012-08-01 +----------------------- +- Introduced a 'user_password_reset_timeout' variable to allow the 24-hour + expiration for user password reset links to be adjusted (API addition). +- Fixed database errors due to ambiguous column names that occurred when + EntityFieldQuery was used in certain situations. +- Changed the drupal_array_get_nested_value() function to return a reference + (API addition). +- Changed the System module's hook_block_info() implementation to assign the + "Main page content" and "System help" blocks to appropriate regions by + default and prevent error messages on the block administration page (data + structure change). +- Fixed regression: Non-node entities couldn't be accessed with + EntityFieldQuery. +- Fixed regression: Optional radio buttons with an empty, non-NULL default + value led to an illegal choice error when none were selected. +- Reorganized the testing framework to split setUp() into specific sub-methods + and fix several regressions in the process. +- Fixed bug which made it impossible to search for strings that have not been + translated into a particular language. +- Renamed the "Field" column on the Manage Fields screen to "Field type", since + the former was confusing and inaccurate (UI change). +- Performance improvement: Removed needless call to system_rebuild_module_data() + in field_sync_field_status(), greatly speeding up bulk module enable/disable. +- Fixed bug which prevented notifications from being sent when core, module, and + theme updates are available. +- Fixed bug which prevented sub-themes from inheriting the default values of + theme settings defined by the base theme. +- Fixed bug which prevented the jQuery UI Datepicker from being localized. +- Made Ajax alert dialogs respect error reporting settings. +- Fixed bug which prevented image styles from being deleted on PHP 5.4. +- Fixed bug: Language detection by domain only worked on port 80. +- Fixed regression: The first plural index on a page was not calculated + correctly. +- Introduced generic entity language support. Entities may now declare their + language property in hook_entity_info(), and modules working with entities + may access the language using entity_language() (API change: + http://drupal.org/node/1626346). +- Added EntityFieldQuery support for taxonomy bundles. +- Fixed issue where field form structure was incomplete if field_access() + returned FALSE. Instead of being incomplete, the form structure now has + #access set to FALSE and field form validation is skipped (data structure + change: http://drupal.org/node/1663020). +- Fixed data loss issue due to field_has_data() returning inconsistent results. + The fix adds an optional DANGEROUS_ACCESS_CHECK_OPT_OUT tag to entity field + queries which field storage engines can respond to (API addition: + http://drupal.org/node/1597378). +- Fixed notice: Undefined index: default_image in image_field_prepare_view() +- Numerous API documentation improvements. +- Additional automated test coverage. + Drupal 7.14 2012-05-02 ---------------------- - Fixed "integrity constraint" fatal errors when rebuilding registry. @@ -48,7 +99,6 @@ Drupal 7.14 2012-05-02 - system_update_7061() converts filepaths too aggressively. - Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25. - Drupal 7.13 2012-05-02 ---------------------- - Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002. diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index e98347441..a2a6511cf 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,5 +1,4 @@ - -All Drupal code is Copyright 2001 - 2010 by the original authors. +All Drupal code is Copyright 2001 - 2012 by the original authors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,5 +20,25 @@ Drupal includes works under other copyright notices and distributed according to the terms of the GNU General Public License or a compatible license, including: - jQuery - Copyright (c) 2008 - 2009 John Resig +Javascript + + Farbtastic - Copyright (c) 2010 Matt Farina + + jQuery - Copyright (c) 2010 John Resig + + jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman + + jQuery Cookie - Copyright (c) 2006 Klaus Hartl + + jQuery Form - Copyright (c) 2010 Mike Alsup + + jQuery Once - Copyright (c) 2009 Konstantin K�fer + + jQuery UI - Copyright (c) 2010 by the original authors + (http://jqueryui.com/about) + + Sizzle.js - Copyright (c) 2010 The Dojo Foundation (http://sizzlejs.com/) + +PHP + ArchiveTar - Copyright (c) 1997 - 2008 Vincent Blavet diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index aff25901f..aa1421533 100644 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -11,6 +11,7 @@ The branch maintainers for Drupal 7 are: - Dries Buytaert 'dries' <http://drupal.org/user/1> - Angela Byron 'webchick' <http://drupal.org/user/24967> +- David Rothstein 'David_Rothstein' <http://drupal.org/user/124982> Component maintainers @@ -23,7 +24,6 @@ maintainer. Current component maintainers for Drupal 7: Ajax system - Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040> -- Randy Fay 'rfay' <http://drupal.org/user/30906> - Earl Miles 'merlinofchaos' <http://drupal.org/user/26979> Base system @@ -147,6 +147,10 @@ User experience and usability - Roy Scholten 'yoroy' <http://drupal.org/user/41502> - Bojhan Somers 'Bojhan' <http://drupal.org/user/87969> +Node Access +- Moshe Weitzman 'moshe weitzman' <http://drupal.org/user/23> +- Ken Rickard 'agentrickard' <http://drupal.org/user/20975> +- Jess Myrbo 'xjm' <http://drupal.org/user/65776> Module maintainers ------------------ diff --git a/UPGRADE.txt b/UPGRADE.txt index c993df7e1..e870ff0f0 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -141,7 +141,7 @@ following the instructions in the INTRODUCTION section at the top of this file: download Drupal 6.x and follow the instructions in its UPGRADE.txt. This document only applies for upgrades from 6.x to 7.x. -3. In addition to updating to the latest available version of Drupal 7.x core, +3. In addition to updating to the latest available version of Drupal 6.x core, you must also upgrade all of your contributed modules for Drupal to their latest Drupal 6.x versions. diff --git a/authorize.php b/authorize.php index 3c0bd7b36..d14fa6e59 100644 --- a/authorize.php +++ b/authorize.php @@ -60,7 +60,6 @@ function authorize_access_allowed() { // *** Real work of the script begins here. *** require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; -require_once DRUPAL_ROOT . '/includes/session.inc'; require_once DRUPAL_ROOT . '/includes/common.inc'; require_once DRUPAL_ROOT . '/includes/file.inc'; require_once DRUPAL_ROOT . '/includes/module.inc'; diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 61edce216..b99e4b93f 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '7.14'); +define('VERSION', '7.15'); /** * Core API compatibility. @@ -1773,22 +1773,37 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO } /** - * Sets a message which reflects the status of the performed operation. + * Sets a message to display to the user. * - * If the function is called with no arguments, this function returns all set - * messages without clearing them. + * Messages are stored in a session variable and displayed in page.tpl.php via + * the $messages theme variable. * - * @param $message - * The message to be displayed to the user. For consistency with other - * messages, it should begin with a capital letter and end with a period. - * @param $type - * The type of the message. One of the following values are possible: + * Example usage: + * @code + * drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); + * @endcode + * + * @param string $message + * (optional) The translated message to be displayed to the user. For + * consistency with other messages, it should begin with a capital letter and + * end with a period. + * @param string $type + * (optional) The message's type. Defaults to 'status'. These values are + * supported: * - 'status' * - 'warning' * - 'error' - * @param $repeat - * If this is FALSE and the message is already set, then the message won't - * be repeated. + * @param bool $repeat + * (optional) If this is FALSE and the message is already set, then the + * message won't be repeated. Defaults to TRUE. + * + * @return array|null + * A multidimensional array with keys corresponding to the set message types. + * The indexed array values of each contain the set messages for that type. + * Or, if there are no messages set, the function returns NULL. + * + * @see drupal_get_messages() + * @see theme_status_messages() */ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) { if ($message) { @@ -2725,7 +2740,7 @@ function request_path() { return $path; } - if (isset($_GET['q'])) { + if (isset($_GET['q']) && is_string($_GET['q'])) { // This is a request with a ?q=foo/bar query string. $_GET['q'] is // overwritten in drupal_path_initialize(), but request_path() is called // very early in the bootstrap process, so the original value is saved in @@ -2856,7 +2871,7 @@ function ip_address() { } /** - * @ingroup schemaapi + * @addtogroup schemaapi * @{ */ @@ -2978,12 +2993,12 @@ function drupal_get_complete_schema($rebuild = FALSE) { } /** - * @} End of "ingroup schemaapi". + * @} End of "addtogroup schemaapi". */ /** - * @ingroup registry + * @addtogroup registry * @{ */ @@ -3145,7 +3160,7 @@ function registry_update() { } /** - * @} End of "ingroup registry". + * @} End of "addtogroup registry". */ /** diff --git a/includes/common.inc b/includes/common.inc index 1ba7b8207..dfa9153a2 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -647,20 +647,23 @@ function drupal_encode_path($path) { * callback. * * @param $path - * A Drupal path or a full URL. + * (optional) A Drupal path or a full URL, which will be passed to url() to + * compute the redirect for the URL. * @param $options - * An associative array of additional URL options to pass to url(). + * (optional) An associative array of additional URL options to pass to url(). * @param $http_response_code - * Valid values for an actual "goto" as per RFC 2616 section 10.3 are: - * - 301 Moved Permanently (the recommended value for most redirects) - * - 302 Found (default in Drupal and PHP, sometimes used for spamming search - * engines) - * - 303 See Other - * - 304 Not Modified - * - 305 Use Proxy - * - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance") - * Note: Other values are defined by RFC 2616, but are rarely used and poorly - * supported. + * (optional) The HTTP status code to use for the redirection, defaults to + * 302. The valid values for 3xx redirection status codes are defined in + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3 RFC 2616 @endlink + * and the + * @link http://tools.ietf.org/html/draft-reschke-http-status-308-07 draft for the new HTTP status codes: @endlink + * - 301: Moved Permanently (the recommended value for most redirects). + * - 302: Found (default in Drupal and PHP, sometimes used for spamming search + * engines). + * - 303: See Other. + * - 304: Not Modified. + * - 305: Use Proxy. + * - 307: Temporary Redirect. * * @see drupal_get_destination() * @see url() @@ -1099,7 +1102,7 @@ function fix_gpc_magic() { /** * Verifies the syntax of the given e-mail address. * - * Empty e-mail addresses are allowed. See RFC 2822 for details. + * See @link http://tools.ietf.org/html/rfc5321 RFC 5321 @endlink for details. * * @param $mail * A string containing an e-mail address. @@ -2041,8 +2044,9 @@ function format_username($account) { * alternative than url(). * * @param $path - * The internal path or external URL being linked to, such as "node/34" or - * "http://example.com/foo". A few notes: + * (optional) The internal path or external URL being linked to, such as + * "node/34" or "http://example.com/foo". The default value is equivalent to + * passing in '<front>'. A few notes: * - If you provide a full URL, it will be considered an external URL. * - If you provide only the path (e.g. "node/34"), it will be * considered an internal link. In this case, it should be a system URL, @@ -2058,7 +2062,8 @@ function format_username($account) { * include them in $path, or use $options['query'] to let this function * URL encode them. * @param $options - * An associative array of additional options, with the following elements: + * (optional) An associative array of additional options, with the following + * elements: * - 'query': An array of query key/value-pairs (without any URL-encoding) to * append to the URL. * - 'fragment': A fragment identifier (named anchor) to append to the URL. @@ -5653,8 +5658,9 @@ function drupal_render_page($page) { * any children, it is the responsibility of the theme function to render * these children. For elements that are not allowed to have any children, * e.g. buttons or textfields, the theme function can be used to render the - * element itself. If #theme is not present and the element has children, they - * are rendered and concatenated into a string by drupal_render_children(). + * element itself. If #theme is not present and the element has children, each + * child is itself rendered by a call to drupal_render(), and the results are + * concatenated. * * The #theme_wrappers property contains an array of theme functions which will * be called, in order, after #theme has run. These can be used to add further @@ -6216,7 +6222,19 @@ function element_info_property($type, $property_name, $default = NULL) { } /** - * Function used by uasort to sort structured arrays by weight, without the property weight prefix. + * Sorts a structured array by the 'weight' element. + * + * Note that the sorting is by the 'weight' array element, not by the render + * element property '#weight'. + * + * Callback for uasort() used in various functions. + * + * @param $a + * First item for comparison. The compared items should be associative arrays + * that optionally include a 'weight' element. For items without a 'weight' + * element, a default value of 0 will be used. + * @param $b + * Second item for comparison. */ function drupal_sort_weight($a, $b) { $a_weight = (is_array($a) && isset($a['weight'])) ? $a['weight'] : 0; @@ -6490,7 +6508,7 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f * * @see drupal_array_set_nested_value() */ -function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) { +function &drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) { $ref = &$array; foreach ($parents as $parent) { if (is_array($ref) && array_key_exists($parent, $ref)) { @@ -6498,7 +6516,8 @@ function drupal_array_get_nested_value(array &$array, array $parents, &$key_exis } else { $key_exists = FALSE; - return NULL; + $null = NULL; + return $null; } } $key_exists = TRUE; @@ -6748,7 +6767,7 @@ function drupal_common_theme() { } /** - * @ingroup schemaapi + * @addtogroup schemaapi * @{ */ @@ -6896,6 +6915,10 @@ function drupal_schema_fields_sql($table, $prefix = NULL) { /** * Saves (inserts or updates) a record to the database based upon the schema. * + * Do not use drupal_write_record() within hook_update_N() functions, since the + * database schema cannot be relied upon when a user is running a series of + * updates. Instead, use db_insert() or db_update() to save the record. + * * @param $table * The name of the table; this must be defined by a hook_schema() * implementation. @@ -7051,7 +7074,7 @@ function drupal_write_record($table, &$record, $primary_keys = array()) { } /** - * @} End of "ingroup schemaapi". + * @} End of "addtogroup schemaapi". */ /** @@ -7774,6 +7797,44 @@ function entity_label($entity_type, $entity) { return $label; } +/** + * Returns the language of an entity. + * + * @param $entity_type + * The entity type; e.g., 'node' or 'user'. + * @param $entity + * The entity for which to get the language. + * + * @return + * A valid language code or NULL if the entity has no language support. + */ +function entity_language($entity_type, $entity) { + $info = entity_get_info($entity_type); + + // Invoke the callback to get the language. If there is no callback, try to + // get it from a property of the entity, otherwise NULL. + if (isset($info['language callback']) && function_exists($info['language callback'])) { + $langcode = $info['language callback']($entity_type, $entity); + } + elseif (!empty($info['entity keys']['language']) && isset($entity->{$info['entity keys']['language']})) { + $langcode = $entity->{$info['entity keys']['language']}; + } + else { + // The value returned in D8 would be LANGUAGE_NONE, we cannot use it here to + // preserve backward compatibility. In fact this function has been + // introduced very late in the D7 life cycle, mainly as the proper default + // for field_attach_form(). By returning LANGUAGE_NONE when no language + // information is available, we would introduce a potentially BC-breaking + // API change, since field_attach_form() defaults to the default language + // instead of LANGUAGE_NONE. Moreover this allows us to distinguish between + // entities that have no language specified from ones that do not have + // language support at all. + $langcode = NULL; + } + + return $langcode; +} + /** * Helper function for attaching field API validation to entity forms. */ diff --git a/includes/database/database.inc b/includes/database/database.inc index 6efe298d2..5bcae67a3 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -41,7 +41,7 @@ * $result = db_query_range('SELECT n.nid, n.title, n.created * FROM {node} n WHERE n.uid = :uid', 0, 10, array(':uid' => $uid)); * foreach ($result as $record) { - * // Perform operations on $node->title, etc. here. + * // Perform operations on $record->title, etc. here. * } * @endcode * Curly braces are used around "node" to provide table prefixing via @@ -988,6 +988,9 @@ abstract class DatabaseConnection extends PDO { * @param $name * Optional name of the savepoint. * + * @return DatabaseTransaction + * A DatabaseTransaction object. + * * @see DatabaseTransaction */ public function startTransaction($name = '') { @@ -2669,7 +2672,7 @@ function db_condition($conjunction) { /** - * @ingroup schemaapi + * @addtogroup schemaapi * @{ */ @@ -2986,7 +2989,7 @@ function db_change_field($table, $field, $field_new, $spec, $keys_new = array()) } /** - * @} End of "ingroup schemaapi". + * @} End of "addtogroup schemaapi". */ /** diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 7278a2bc8..7ad019e58 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -198,5 +198,5 @@ class DatabaseConnection_mysql extends DatabaseConnection { /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/mysql/query.inc b/includes/database/mysql/query.inc index 888b6a5a4..2609aba0c 100644 --- a/includes/database/mysql/query.inc +++ b/includes/database/mysql/query.inc @@ -1,7 +1,7 @@ <?php /** - * @ingroup database + * @addtogroup database * @{ */ @@ -103,5 +103,5 @@ class TruncateQuery_mysql extends TruncateQuery { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc index d6aea4d94..949cf4e8a 100644 --- a/includes/database/mysql/schema.inc +++ b/includes/database/mysql/schema.inc @@ -7,7 +7,7 @@ /** - * @ingroup schemaapi + * @addtogroup schemaapi * @{ */ @@ -532,5 +532,5 @@ class DatabaseSchema_mysql extends DatabaseSchema { } /** - * @} End of "ingroup schemaapi". + * @} End of "addtogroup schemaapi". */ diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc index d80b47551..79c16b212 100644 --- a/includes/database/pgsql/database.inc +++ b/includes/database/pgsql/database.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -208,5 +208,5 @@ class DatabaseConnection_pgsql extends DatabaseConnection { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/pgsql/select.inc b/includes/database/pgsql/select.inc index d1d838281..f6a83db7f 100644 --- a/includes/database/pgsql/select.inc +++ b/includes/database/pgsql/select.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -103,6 +103,6 @@ class SelectQuery_pgsql extends SelectQuery { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/prefetch.inc b/includes/database/prefetch.inc index 4f2b19d1f..3b36a4e10 100644 --- a/includes/database/prefetch.inc +++ b/includes/database/prefetch.inc @@ -9,7 +9,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -502,6 +502,6 @@ class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/query.inc b/includes/database/query.inc index 750aea7a0..612985e02 100644 --- a/includes/database/query.inc +++ b/includes/database/query.inc @@ -1,7 +1,7 @@ <?php /** - * @ingroup database + * @addtogroup database * @{ */ @@ -1955,5 +1955,5 @@ class DatabaseCondition implements QueryConditionInterface, Countable { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/select.inc b/includes/database/select.inc index 7e2af85e7..e036904be 100644 --- a/includes/database/select.inc +++ b/includes/database/select.inc @@ -1,7 +1,7 @@ <?php /** - * @ingroup database + * @addtogroup database * @{ */ @@ -1609,5 +1609,5 @@ class SelectQuery extends Query implements SelectQueryInterface { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index ea91e9143..b302b3e32 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -515,5 +515,5 @@ class DatabaseStatement_sqlite extends DatabaseStatementPrefetch implements Iter } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/sqlite/query.inc b/includes/database/sqlite/query.inc index 6b8a72f2a..74ff9ba20 100644 --- a/includes/database/sqlite/query.inc +++ b/includes/database/sqlite/query.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -156,5 +156,5 @@ class TruncateQuery_sqlite extends TruncateQuery { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/database/sqlite/select.inc b/includes/database/sqlite/select.inc index fb926ef04..9037a0e7a 100644 --- a/includes/database/sqlite/select.inc +++ b/includes/database/sqlite/select.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup database + * @addtogroup database * @{ */ @@ -21,7 +21,7 @@ class SelectQuery_sqlite extends SelectQuery { } /** - * @} End of "ingroup database". + * @} End of "addtogroup database". */ diff --git a/includes/entity.inc b/includes/entity.inc index ae7807794..832abe2fd 100644 --- a/includes/entity.inc +++ b/includes/entity.inc @@ -634,6 +634,9 @@ class EntityFieldQuery { /** * Adds a condition on field values. + * + * Note that entities with empty field values will be excluded from the + * EntityFieldQuery results when using this method. * * @param $field * Either a field name or a field array. @@ -858,7 +861,9 @@ class EntityFieldQuery { * Orders the result set by a given field column. * * If called multiple times, the query will order by each specified column in - * the order this method is called. + * the order this method is called. Note that entities with empty field + * values will be excluded from the EntityFieldQuery results when using this + * method. * * @param $field * Either a field name or a field array. @@ -1199,7 +1204,7 @@ class EntityFieldQuery { $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type)); // Process the property conditions. foreach ($this->propertyConditions as $property_condition) { - $this->addCondition($select_query, "$base_table." . $property_condition['column'], $property_condition); + $this->addCondition($select_query, $base_table . '.' . $property_condition['column'], $property_condition); } // Process the four possible entity condition. // The id field is always present in entity keys. @@ -1207,7 +1212,7 @@ class EntityFieldQuery { $id_map['entity_id'] = $sql_field; $select_query->addField($base_table, $sql_field, 'entity_id'); if (isset($this->entityConditions['entity_id'])) { - $this->addCondition($select_query, $sql_field, $this->entityConditions['entity_id']); + $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['entity_id']); } // If there is a revision key defined, use it. @@ -1215,7 +1220,7 @@ class EntityFieldQuery { $sql_field = $entity_info['entity keys']['revision']; $select_query->addField($base_table, $sql_field, 'revision_id'); if (isset($this->entityConditions['revision_id'])) { - $this->addCondition($select_query, $sql_field, $this->entityConditions['revision_id']); + $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['revision_id']); } } else { @@ -1240,7 +1245,13 @@ class EntityFieldQuery { } $id_map['bundle'] = $sql_field; if (isset($this->entityConditions['bundle'])) { - $this->addCondition($select_query, $sql_field, $this->entityConditions['bundle'], $having); + if (!empty($entity_info['entity keys']['bundle'])) { + $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['bundle'], $having); + } + else { + // This entity has no bundle, so invalidate the query. + $select_query->where('1 = 0'); + } } // Order the query. @@ -1253,7 +1264,7 @@ class EntityFieldQuery { $select_query->orderBy($id_map[$key], $order['direction']); } elseif ($order['type'] == 'property') { - $select_query->orderBy("$base_table." . $order['specifier'], $order['direction']); + $select_query->orderBy($base_table . '.' . $order['specifier'], $order['direction']); } } diff --git a/includes/errors.inc b/includes/errors.inc index f62bf06a5..9d0df0544 100644 --- a/includes/errors.inc +++ b/includes/errors.inc @@ -230,8 +230,10 @@ function _drupal_log_error($error, $fatal = FALSE) { if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { if ($fatal) { - // When called from JavaScript, simply output the error message. - print t('%type: !message in %function (line %line of %file).', $error); + if (error_displayable($error)) { + // When called from JavaScript, simply output the error message. + print t('%type: !message in %function (line %line of %file).', $error); + } exit; } } diff --git a/includes/form.inc b/includes/form.inc index 3ef32ab41..d7350b3e2 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -164,6 +164,8 @@ function drupal_get_form($form_id) { * automatically loaded by form_get_cache(). By default the current menu * router item's 'file' definition is added, if any. Use * form_load_include() to add include files from a form constructor. + * - form_id: Identification of the primary form being constructed and + * processed. * - base_form_id: Identification for a base form, as declared in a * hook_forms() implementation. * - rebuild_info: Internal. Similar to 'build_info', but pertaining to @@ -572,7 +574,7 @@ function form_state_keys_no_cache() { } /** - * Ensures an include file is loaded loaded whenever the form is processed. + * Ensures an include file is loaded whenever the form is processed. * * Example: * @code @@ -721,6 +723,9 @@ function drupal_form_submit($form_id, &$form_state) { function drupal_retrieve_form($form_id, &$form_state) { $forms = &drupal_static(__FUNCTION__); + // Record the $form_id. + $form_state['build_info']['form_id'] = $form_id; + // Record the filepath of the include file containing the original form, so // the form builder callbacks can be loaded when the form is being rebuilt // from cache on a different path (such as 'system/ajax'). See @@ -977,6 +982,10 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { '#value' => $form['#build_id'], '#id' => $form['#build_id'], '#name' => 'form_build_id', + // Form processing and validation requires this value, so ensure the + // submitted form value appears literally, regardless of custom #tree + // and #parents being set elsewhere. + '#parents' => array('form_build_id'), ); // Add a token, based on either #token or form_id, to any form displayed to @@ -1000,6 +1009,10 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { '#id' => drupal_html_id('edit-' . $form_id . '-form-token'), '#type' => 'token', '#default_value' => drupal_get_token($form['#token']), + // Form processing and validation requires this value, so ensure the + // submitted form value appears literally, regardless of custom #tree + // and #parents being set elsewhere. + '#parents' => array('form_token'), ); } } @@ -1009,6 +1022,10 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { '#type' => 'hidden', '#value' => $form_id, '#id' => drupal_html_id("edit-$form_id"), + // Form processing and validation requires this value, so ensure the + // submitted form value appears literally, regardless of custom #tree + // and #parents being set elsewhere. + '#parents' => array('form_id'), ); } if (!isset($form['#id'])) { @@ -1086,7 +1103,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { * A keyed array containing the current state of the form. The current * user-submitted data is stored in $form_state['values'], though * form validation functions are passed an explicit copy of the - * values for the sake of simplicity. Validation handlers can also + * values for the sake of simplicity. Validation handlers can also use * $form_state to pass information on to submit handlers. For example: * $form_state['data_for_submission'] = $data; * This technique is useful when validation requires file parsing, @@ -2149,12 +2166,12 @@ function form_state_values_clean(&$form_state) { // $form_state['values']['foo']['bar'], which is the level where we can // unset 'baz' (that is stored in $last_parent). $parents = $button['#parents']; - $values = &$form_state['values']; $last_parent = array_pop($parents); - foreach ($parents as $parent) { - $values = &$values[$parent]; + $key_exists = NULL; + $values = &drupal_array_get_nested_value($form_state['values'], $parents, $key_exists); + if ($key_exists && is_array($values)) { + unset($values[$last_parent]); } - unset($values[$last_parent]); } } @@ -2339,30 +2356,27 @@ function form_type_tableselect_value($element, $input = FALSE) { */ function form_type_radios_value(&$element, $input = FALSE) { if ($input !== FALSE) { - // There may not be a submitted value for multiple radio buttons, if none of - // the options was checked by default. If there is no submitted input value - // for this element (NULL), _form_builder_handle_input_element() - // automatically attempts to use the #default_value (if set) or an empty - // string (''). However, an empty string would fail validation in - // _form_validate(), in case it is not contained in the list of allowed - // values in #options. - if (!isset($input)) { - // Signify a garbage value to disable the #default_value handling and take - // over NULL as #value. - $element['#has_garbage_value'] = TRUE; - // There was a user submission so validation is a must. If this element is - // #required, then an appropriate error message will be output. While an - // optional #type 'radios' does not necessarily make sense from a user - // interaction perspective, there may be use-cases for that and it is not - // the job of Form API to artificially limit possibilities. + // When there's user input (including NULL), return it as the value. + // However, if NULL is submitted, _form_builder_handle_input_element() will + // apply the default value, and we want that validated against #options + // unless it's empty. (An empty #default_value, such as NULL or FALSE, can + // be used to indicate that no radio button is selected by default.) + if (!isset($input) && !empty($element['#default_value'])) { $element['#needs_validation'] = TRUE; } - // The value stays the same, but the flags above will ensure it is - // processed properly. return $input; } - elseif (isset($element['#default_value'])) { - return $element['#default_value']; + else { + // For default value handling, simply return #default_value. Additionally, + // for a NULL default value, set #has_garbage_value to prevent + // _form_builder_handle_input_element() converting the NULL to an empty + // string, so that code can distinguish between nothing selected and the + // selection of a radio button whose value is an empty string. + $value = isset($element['#default_value']) ? $element['#default_value'] : NULL; + if (!isset($value)) { + $element['#has_garbage_value'] = TRUE; + } + return $value; } } diff --git a/includes/graph.inc b/includes/graph.inc index 9ef86a145..35e683057 100644 --- a/includes/graph.inc +++ b/includes/graph.inc @@ -2,12 +2,12 @@ /** * @file - * Directed acyclic graph functions. + * Directed acyclic graph manipulation. */ /** - * Performs a depth-first sort on a directed acyclic graph. + * Performs a depth-first search and sort on a directed acyclic graph. * * @param $graph * A three dimensional associated array, with the first keys being the names @@ -52,7 +52,7 @@ function drupal_depth_first_search(&$graph) { // The components of the graph. 'components' => array(), ); - // Perform the actual sort. + // Perform the actual search. foreach ($graph as $start => $data) { _drupal_depth_first_search($graph, $state, $start); } @@ -72,7 +72,7 @@ function drupal_depth_first_search(&$graph) { } /** - * Performs a depth-first sort on a graph. + * Performs a depth-first search on a graph. * * @param $graph * A three dimensional associated graph array. diff --git a/includes/locale.inc b/includes/locale.inc index 7fb8d6424..c168da0a7 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -279,6 +279,12 @@ function locale_language_from_url($languages) { break; case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN: + // Get only the host, not the port. + $http_host= $_SERVER['HTTP_HOST']; + if (strpos($http_host, ':') !== FALSE) { + $http_host_tmp = explode(':', $http_host); + $http_host = current($http_host_tmp); + } foreach ($languages as $language) { // Skip check if the language doesn't have a domain. if ($language->domain) { @@ -286,7 +292,7 @@ function locale_language_from_url($languages) { // Remove protocol and add http:// so parse_url works $host = 'http://' . str_replace(array('http://', 'https://'), '', $language->domain); $host = parse_url($host, PHP_URL_HOST); - if ($_SERVER['HTTP_HOST'] == $host) { + if ($http_host == $host) { $language_url = $language->language; break; } @@ -1854,7 +1860,16 @@ function _locale_translate_seek() { } $sql_query = db_select('locales_source', 's'); - $sql_query->leftJoin('locales_target', 't', 't.lid = s.lid'); + + $limit_language = NULL; + if ($query['language'] != 'en' && $query['language'] != 'all') { + $sql_query->leftJoin('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(':langcode' => $query['language'])); + $limit_language = $query['language']; + } + else { + $sql_query->leftJoin('locales_target', 't', 't.lid = s.lid'); + } + $sql_query->fields('s', array('source', 'location', 'context', 'lid', 'textgroup')); $sql_query->fields('t', array('translation', 'language')); @@ -1883,12 +1898,6 @@ function _locale_translate_seek() { break; } - $limit_language = NULL; - if ($query['language'] != 'en' && $query['language'] != 'all') { - $sql_query->condition('language', $query['language']); - $limit_language = $query['language']; - } - // Add a condition on the text group. if (!empty($query['group']) && $query['group'] != 'all') { $sql_query->condition('s.textgroup', $query['group']); diff --git a/includes/module.inc b/includes/module.inc index 500bc5ebc..28bca2350 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -178,6 +178,33 @@ function system_list($type) { $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename); } } + foreach ($lists['theme'] as $key => $theme) { + if (!empty($theme->info['base theme'])) { + // Make a list of the theme's base themes. + $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); + // Don't proceed if there was a problem with the root base theme. + if (!current($lists['theme'][$key]->base_themes)) { + continue; + } + // Determine the root base theme. + $base_key = key($lists['theme'][$key]->base_themes); + // Add to the list of sub-themes for each of the theme's base themes. + foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { + $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; + } + // Add the base theme's theme engine info. + $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme'; + } + else { + // A plain theme is its own engine. + $base_key = $key; + if (!isset($lists['theme'][$key]->info['engine'])) { + $lists['theme'][$key]->info['engine'] = 'theme'; + } + } + // Set the theme engine prefix. + $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; + } cache_set('system_list', $lists, 'cache_bootstrap'); } // To avoid a separate database lookup for the filepath, prime the diff --git a/includes/theme.inc b/includes/theme.inc index 51e1075ca..c4b712271 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -734,21 +734,40 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) { * * @return * An associative array of the currently available themes. The keys are the - * names of the themes and the values are objects having the following + * themes' machine names and the values are objects having the following * properties: - * - 'filename': The name of the .info file. - * - 'name': The name of the theme. - * - 'status': 1 for enabled, 0 for disabled themes. - * - 'info': The contents of the .info file. - * - 'stylesheets': A two dimensional array, using the first key for the - * 'media' attribute (e.g. 'all'), the second for the name of the file - * (e.g. style.css). The value is a complete filepath - * (e.g. themes/bartik/style.css). - * - 'scripts': An associative array of JavaScripts, using the filename as key - * and the complete filepath as value. - * - 'engine': The name of the theme engine. - * - 'base theme': The name of the base theme. - */ + * - filename: The filepath and name of the .info file. + * - name: The machine name of the theme. + * - status: 1 for enabled, 0 for disabled themes. + * - info: The contents of the .info file. + * - stylesheets: A two dimensional array, using the first key for the + * media attribute (e.g. 'all'), the second for the name of the file + * (e.g. style.css). The value is a complete filepath (e.g. + * themes/bartik/style.css). Not set if no stylesheets are defined in the + * .info file. + * - scripts: An associative array of JavaScripts, using the filename as key + * and the complete filepath as value. Not set if no scripts are defined in + * the .info file. + * - prefix: The base theme engine prefix. + * - engine: The machine name of the theme engine. + * - base_theme: If this is a sub-theme, the machine name of the base theme + * defined in the .info file. Otherwise, the element is not set. + * - base_themes: If this is a sub-theme, an associative array of the + * base-theme ancestors of this theme, starting with this theme's base + * theme, then the base theme's own base theme, etc. Each entry has an + * array key equal to the theme's machine name, and a value equal to the + * human-readable theme name; if a theme with matching machine name does + * not exist in the system, the value will instead be NULL (and since the + * system would not know whether that theme itself has a base theme, that + * will end the array of base themes). This is not set if the theme is not + * a sub-theme. + * - sub_themes: An associative array of themes on the system that are + * either direct sub-themes (that is, they declare this theme to be + * their base theme), direct sub-themes of sub-themes, etc. The keys are + * the themes' machine names, and the values are the themes' human-readable + * names. This element is not set if there are no themes on the system that + * declare this theme as their base theme. +*/ function list_themes($refresh = FALSE) { $list = &drupal_static(__FUNCTION__, array()); @@ -803,6 +822,47 @@ function list_themes($refresh = FALSE) { return $list; } +/** + * Find all the base themes for the specified theme. + * + * Themes can inherit templates and function implementations from earlier themes. + * + * @param $themes + * An array of available themes. + * @param $key + * The name of the theme whose base we are looking for. + * @param $used_keys + * A recursion parameter preventing endless loops. + * @return + * Returns an array of all of the theme's ancestors; the first element's value + * will be NULL if an error occurred. + */ +function drupal_find_base_themes($themes, $key, $used_keys = array()) { + $base_key = $themes[$key]->info['base theme']; + // Does the base theme exist? + if (!isset($themes[$base_key])) { + return array($base_key => NULL); + } + + $current_base_theme = array($base_key => $themes[$base_key]->info['name']); + + // Is the base theme itself a child of another theme? + if (isset($themes[$base_key]->info['base theme'])) { + // Do we already know the base themes of this theme? + if (isset($themes[$base_key]->base_themes)) { + return $themes[$base_key]->base_themes + $current_base_theme; + } + // Prevent loops. + if (!empty($used_keys[$base_key])) { + return array($base_key => NULL); + } + $used_keys[$base_key] = TRUE; + return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme; + } + // If we get here, then this is our parent theme. + return $current_base_theme; +} + /** * Generates themed output. * @@ -811,11 +871,28 @@ function list_themes($refresh = FALSE) { * @link themeable theme function or template @endlink, by checking the theme * registry. * - * The first argument to this function is the name of the theme hook. For - * instance, to theme a table, the theme hook name is 'table'. By default, this - * theme hook could be implemented by a function called 'theme_table' or a - * template file called 'table.tpl.php', but hook_theme() can override the - * default function or template name. + * Most commonly, the first argument to this function is the name of the theme + * hook. For instance, to theme a taxonomy term, the theme hook name is + * 'taxonomy_term'. Modules register theme hooks within a hook_theme() + * implementation and provide a default implementation via a function named + * theme_HOOK() (e.g., theme_taxonomy_term()) or via a template file named + * according to the value of the 'template' key registered with the theme hook + * (see hook_theme() for details). Default templates are implemented with the + * PHPTemplate rendering engine and are named the same as the theme hook, with + * underscores changed to hyphens, so for the 'taxonomy_term' theme hook, the + * default template is 'taxonomy-term.tpl.php'. + * + * Themes may also register new theme hooks within a hook_theme() + * implementation, but it is more common for themes to override default + * implementations provided by modules than to register entirely new theme + * hooks. Themes can override a default implementation by implementing a + * function named THEME_HOOK() (for example, the 'bartik' theme overrides the + * default implementation of the 'menu_tree' theme hook by implementing a + * bartik_menu_tree() function), or by adding a template file within its folder + * structure that follows the template naming structure used by the theme's + * rendering engine (for example, since the Bartik theme uses the PHPTemplate + * rendering engine, it overrides the default implementation of the 'page' theme + * hook by containing a 'page.tpl.php' file within its folder structure). * * If the implementation is a template file, several functions are called * before the template file is invoked, to modify the $variables array. These @@ -824,42 +901,44 @@ function list_themes($refresh = FALSE) { * list, HOOK indicates the theme hook name, MODULE indicates a module name, * THEME indicates a theme name, and ENGINE indicates a theme engine name): * - template_preprocess(&$variables, $hook): Creates a default set of variables - * for all theme hooks. - * - template_preprocess_HOOK(&$variables): Should be implemented by - * the module that registers the theme hook, to set up default variables. + * for all theme hooks with template implementations. + * - template_preprocess_HOOK(&$variables): Should be implemented by the module + * that registers the theme hook, to set up default variables. * - MODULE_preprocess(&$variables, $hook): hook_preprocess() is invoked on all * implementing modules. * - MODULE_preprocess_HOOK(&$variables): hook_preprocess_HOOK() is invoked on * all implementing modules, so that modules that didn't define the theme hook * can alter the variables. * - ENGINE_engine_preprocess(&$variables, $hook): Allows the theme engine to - * set necessary variables for all theme hooks. + * set necessary variables for all theme hooks with template implementations. * - ENGINE_engine_preprocess_HOOK(&$variables): Allows the theme engine to set * necessary variables for the particular theme hook. * - THEME_preprocess(&$variables, $hook): Allows the theme to set necessary - * variables for all theme hooks. + * variables for all theme hooks with template implementations. * - THEME_preprocess_HOOK(&$variables): Allows the theme to set necessary * variables specific to the particular theme hook. - * - template_process(&$variables, $hook): Creates a default set of variables - * for all theme hooks. - * - template_process_HOOK(&$variables): This is the first processor specific - * to the theme hook; it should be implemented by the module that registers - * it. + * - template_process(&$variables, $hook): Creates an additional set of default + * variables for all theme hooks with template implementations. The variables + * created in this function are derived from ones created by + * template_preprocess(), but potentially altered by the other preprocess + * functions listed above. For example, any preprocess function can add to or + * modify the $variables['attributes_array'] variable, and after all of them + * have finished executing, template_process() flattens it into a + * $variables['attributes'] string for convenient use by templates. + * - template_process_HOOK(&$variables): Should be implemented by the module + * that registers the theme hook, if it needs to perform additional variable + * processing after all preprocess functions have finished. * - MODULE_process(&$variables, $hook): hook_process() is invoked on all * implementing modules. * - MODULE_process_HOOK(&$variables): hook_process_HOOK() is invoked on * on all implementing modules, so that modules that didn't define the theme * hook can alter the variables. - * - ENGINE_engine_process(&$variables, $hook): Allows the theme engine to set - * necessary variables for all theme hooks. - * - ENGINE_engine_process_HOOK(&$variables): Allows the theme engine to set - * necessary variables for the particular theme hook. - * - ENGINE_process(&$variables, $hook): Allows the theme engine to process the - * variables. - * - ENGINE_process_HOOK(&$variables): Allows the theme engine to process the - * variables specific to the theme hook. + * - ENGINE_engine_process(&$variables, $hook): Allows the theme engine to + * process variables for all theme hooks with template implementations. + * - ENGINE_engine_process_HOOK(&$variables): Allows the theme engine to process + * the variables specific to the theme hook. * - THEME_process(&$variables, $hook): Allows the theme to process the - * variables. + * variables for all theme hooks with template implementations. * - THEME_process_HOOK(&$variables): Allows the theme to process the * variables specific to the theme hook. * @@ -911,6 +990,9 @@ function list_themes($refresh = FALSE) { * An HTML string representing the themed output. * * @see themeable + * @see hook_theme() + * @see template_preprocess() + * @see template_process() */ function theme($hook, $variables = array()) { // If called before all modules are loaded, we do not necessarily have a full @@ -1473,7 +1555,7 @@ function theme_disable($theme_list) { } /** - * @ingroup themeable + * @addtogroup themeable * @{ */ @@ -2172,7 +2254,7 @@ function theme_indentation($variables) { } /** - * @} End of "ingroup themeable". + * @} End of "addtogroup themeable". */ /** @@ -2218,11 +2300,15 @@ function _theme_table_cell($cell, $header = FALSE) { /** * Adds a default set of helper variables for variable processors and templates. - * This comes in before any other preprocess function which makes it possible to - * be used in default theme implementations (non-overridden theme functions). * - * For more detailed information, see theme(). + * This function is called for theme hooks implemented as templates only, not + * for theme hooks implemented as functions. This preprocess function is the + * first in the sequence of preprocessing and processing functions that is + * called when preparing variables for a template. See theme() for more details + * about the full sequence. * + * @see theme() + * @see template_process() */ function template_preprocess(&$variables, $hook) { global $user; @@ -2299,10 +2385,19 @@ function _template_preprocess_default_variables() { } /** - * A default process function used to alter variables as late as possible. + * Adds helper variables derived from variables defined during preprocessing. * - * For more detailed information, see theme(). + * When preparing variables for a theme hook implementation, all 'preprocess' + * functions run first, then all 'process' functions (see theme() for details + * about the full sequence). * + * This function serializes array variables manipulated during the preprocessing + * phase into strings for convenient use by templates. As with + * template_preprocess(), this function does not get called for theme hooks + * implemented as functions. + * + * @see theme() + * @see template_preprocess() */ function template_process(&$variables, $hook) { // Flatten out classes. diff --git a/includes/update.inc b/includes/update.inc index 24001a085..d8fec64df 100644 --- a/includes/update.inc +++ b/includes/update.inc @@ -1488,5 +1488,5 @@ function update_retrieve_dependencies() { */ /** - * @} End of "defgroup update-api-6.x-to-7.x" + * @} End of "defgroup update-api-6.x-to-7.x". */ diff --git a/misc/autocomplete.js b/misc/autocomplete.js index 267d4b79b..8f7ac6011 100644 --- a/misc/autocomplete.js +++ b/misc/autocomplete.js @@ -99,10 +99,12 @@ Drupal.jsAC.prototype.onkeyup = function (input, e) { return true; default: // All other keys. - if (input.value.length > 0) + if (input.value.length > 0 && !input.readOnly) { this.populatePopup(); - else + } + else { this.hidePopup(e.keyCode); + } return true; } }; diff --git a/misc/states.js b/misc/states.js index 00eeba17b..594f8187b 100644 --- a/misc/states.js +++ b/misc/states.js @@ -16,10 +16,11 @@ var states = Drupal.states = { */ Drupal.behaviors.states = { attach: function (context, settings) { + var $context = $(context); for (var selector in settings.states) { for (var state in settings.states[selector]) { new states.Dependent({ - element: $(selector), + element: $context.find(selector), state: states.State.sanitize(state), constraints: settings.states[selector][state] }); diff --git a/misc/tableselect.js b/misc/tableselect.js index 5a88ac20c..fee63a9fd 100644 --- a/misc/tableselect.js +++ b/misc/tableselect.js @@ -17,7 +17,8 @@ Drupal.tableSelect = function () { var table = this, checkboxes, lastChecked; var strings = { 'selectAll': Drupal.t('Select all rows in this table'), 'selectNone': Drupal.t('Deselect all rows in this table') }; var updateSelectAll = function (state) { - $('th.select-all input:checkbox', table).each(function () { + // Update table's select-all checkbox (and sticky header's if available). + $(table).prev('table.sticky-header').andSelf().find('th.select-all input:checkbox').each(function() { $(this).attr('title', state ? strings.selectNone : strings.selectAll); this.checked = state; }); diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info index 4ffccff46..75fe82b95 100644 --- a/modules/aggregator/aggregator.info +++ b/modules/aggregator/aggregator.info @@ -7,8 +7,8 @@ files[] = aggregator.test configure = admin/config/services/aggregator/settings stylesheets[all][] = aggregator.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/aggregator/tests/aggregator_test.info b/modules/aggregator/tests/aggregator_test.info index 3cf09c5d2..29baa019d 100644 --- a/modules/aggregator/tests/aggregator_test.info +++ b/modules/aggregator/tests/aggregator_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/block/block.info b/modules/block/block.info index 10f3ede51..386997798 100644 --- a/modules/block/block.info +++ b/modules/block/block.info @@ -6,8 +6,8 @@ core = 7.x files[] = block.test configure = admin/structure/block -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/block/block.install b/modules/block/block.install index b2ab477d9..a78c885e7 100644 --- a/modules/block/block.install +++ b/modules/block/block.install @@ -456,6 +456,15 @@ function block_update_7007() { )); } +/** + * @} End of "addtogroup updates-6.x-to-7.x". + */ + +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Update database to match Drupal 7 schema. */ @@ -464,5 +473,5 @@ function block_update_7008() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-7.x-extra". */ diff --git a/modules/block/block.test b/modules/block/block.test index cdd0d4589..8e6e47042 100644 --- a/modules/block/block.test +++ b/modules/block/block.test @@ -97,7 +97,7 @@ class BlockTestCase extends DrupalWebTestCase { // Set visibility only for authenticated users, to verify delete functionality. $edit = array(); - $edit['roles[2]'] = TRUE; + $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE; $this->drupalPost('admin/structure/block/manage/block/' . $bid . '/configure', $edit, t('Save block')); // Delete the created custom block & verify that it's been deleted and no longer appearing on the page. @@ -171,7 +171,7 @@ class BlockTestCase extends DrupalWebTestCase { // authenticated users. $edit = array(); $edit['pages'] = 'user*'; - $edit['roles[2]'] = TRUE; + $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = TRUE; $this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', $edit, t('Save block')); // Move block to the first sidebar. diff --git a/modules/block/tests/block_test.info b/modules/block/tests/block_test.info index 2ea89b0bc..8a34798ce 100644 --- a/modules/block/tests/block_test.info +++ b/modules/block/tests/block_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/block/tests/themes/block_test_theme/block_test_theme.info b/modules/block/tests/themes/block_test_theme/block_test_theme.info index bcf507f12..b7381ae79 100644 --- a/modules/block/tests/themes/block_test_theme/block_test_theme.info +++ b/modules/block/tests/themes/block_test_theme/block_test_theme.info @@ -13,8 +13,8 @@ regions[footer] = Footer regions[highlighted] = Highlighted regions[help] = Help -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/blog/blog.info b/modules/blog/blog.info index ce4ef4a90..1ff2214fe 100644 --- a/modules/blog/blog.info +++ b/modules/blog/blog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = blog.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/book/book.info b/modules/book/book.info index 2af6a01d8..166521794 100644 --- a/modules/book/book.info +++ b/modules/book/book.info @@ -7,8 +7,8 @@ files[] = book.test configure = admin/content/book/settings stylesheets[all][] = book.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/book/book.module b/modules/book/book.module index 61061f011..1fb0c0b11 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -213,7 +213,17 @@ function _book_outline_access($node) { * @see book_menu() */ function _book_outline_remove_access($node) { - return isset($node->book) && ($node->book['bid'] != $node->nid) && _book_outline_access($node); + return _book_node_is_removable($node) && _book_outline_access($node); +} + +/** + * Determines if a node can be removed from the book. + * + * A node can be removed from a book if it is actually in a book and it either + * is not a top-level page or is a top-level page with no children. + */ +function _book_node_is_removable($node) { + return (!empty($node->book['bid']) && (($node->book['bid'] != $node->nid) || !$node->book['has_children'])); } /** @@ -518,13 +528,12 @@ function _book_add_form_elements(&$form, &$form_state, $node) { '#collapsed' => TRUE, '#group' => 'additional_settings', '#attributes' => array( - 'class' => array('book-form'), + 'class' => array('book-outline-form'), ), '#attached' => array( 'js' => array(drupal_get_path('module', 'book') . '/book.js'), ), '#tree' => TRUE, - '#attributes' => array('class' => array('book-outline-form')), ); foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) { $form['book'][$key] = array( diff --git a/modules/book/book.pages.inc b/modules/book/book.pages.inc index 5a05c9f1d..583eb7a81 100644 --- a/modules/book/book.pages.inc +++ b/modules/book/book.pages.inc @@ -137,7 +137,7 @@ function book_outline_form($form, &$form_state, $node) { $form['remove'] = array( '#type' => 'submit', '#value' => t('Remove from book outline'), - '#access' => $node->nid != $node->book['bid'] && $node->book['bid'], + '#access' => _book_node_is_removable($node), '#weight' => 20, '#submit' => array('book_remove_button_submit'), ); @@ -216,8 +216,7 @@ function book_remove_form($form, &$form_state, $node) { */ function book_remove_form_submit($form, &$form_state) { $node = $form['#node']; - if ($node->nid != $node->book['bid']) { - // Only allowed when this is not a book (top-level page). + if (_book_node_is_removable($node)) { menu_link_delete($node->book['mlid']); db_delete('book') ->condition('nid', $node->nid) diff --git a/modules/book/book.test b/modules/book/book.test index 6c351b8ec..d1f527387 100644 --- a/modules/book/book.test +++ b/modules/book/book.test @@ -32,7 +32,7 @@ class BookTestCase extends DrupalWebTestCase { // Create users. $this->book_author = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books')); $this->web_user = $this->drupalCreateUser(array('access printer-friendly version', 'node test view')); - $this->admin_user = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks', 'administer permissions')); + $this->admin_user = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks', 'administer permissions', 'administer book outlines', 'node test view')); } /** @@ -279,8 +279,8 @@ class BookTestCase extends DrupalWebTestCase { // Give anonymous users the permission 'node test view'. $edit = array(); - $edit['1[node test view]'] = TRUE; - $this->drupalPost('admin/people/permissions/1', $edit, t('Save permissions')); + $edit[DRUPAL_ANONYMOUS_RID . '[node test view]'] = TRUE; + $this->drupalPost('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, $edit, t('Save permissions')); $this->assertText(t('The changes have been saved.'), t("Permission 'node test view' successfully assigned to anonymous users.")); // Test correct display of the block. @@ -315,8 +315,8 @@ class BookTestCase extends DrupalWebTestCase { // Give anonymous users the permission 'node test view'. $edit = array(); - $edit['1[node test view]'] = TRUE; - $this->drupalPost('admin/people/permissions/1', $edit, t('Save permissions')); + $edit[DRUPAL_ANONYMOUS_RID . '[node test view]'] = TRUE; + $this->drupalPost('admin/people/permissions/' . DRUPAL_ANONYMOUS_RID, $edit, t('Save permissions')); $this->assertText(t('The changes have been saved.'), t('Permission \'node test view\' successfully assigned to anonymous users.')); // Create a book. @@ -332,4 +332,29 @@ class BookTestCase extends DrupalWebTestCase { $this->drupalGet('node/' . $this->book->nid); $this->assertText($block_title, t('Book navigation block is displayed to anonymous users.')); } + + /** + * Tests the access for deleting top-level book nodes. + */ + function testBookDelete() { + $nodes = $this->createBook(); + $this->drupalLogin($this->admin_user); + $edit = array(); + + // Test access to delete top-level and child book nodes. + $this->drupalGet('node/' . $this->book->nid . '/outline/remove'); + $this->assertResponse('403', t('Deleting top-level book node properly forbidden.')); + $this->drupalPost('node/' . $nodes[4]->nid . '/outline/remove', $edit, t('Remove')); + $node4 = node_load($nodes[4]->nid, NULL, TRUE); + $this->assertTrue(empty($node4->book), t('Deleting child book node properly allowed.')); + + // Delete all child book nodes and retest top-level node deletion. + foreach ($nodes as $node) { + $nids[] = $node->nid; + } + node_delete_multiple($nids); + $this->drupalPost('node/' . $this->book->nid . '/outline/remove', $edit, t('Remove')); + $node = node_load($this->book->nid, NULL, TRUE); + $this->assertTrue(empty($node->book), t('Deleting childless top-level book node properly allowed.')); + } } diff --git a/modules/color/color.info b/modules/color/color.info index dd13c03a7..5e4912302 100644 --- a/modules/color/color.info +++ b/modules/color/color.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = color.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/color/color.install b/modules/color/color.install index 2a6b9cdd1..3a9aea355 100644 --- a/modules/color/color.install +++ b/modules/color/color.install @@ -41,6 +41,11 @@ function color_requirements($phase) { return $requirements; } +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Warn site administrator if unsafe CSS color codes are found in the database. */ @@ -55,3 +60,7 @@ function color_update_7001() { } } } + +/** + * @} End of "addtogroup updates-7.x-extra". + */ diff --git a/modules/comment/comment.info b/modules/comment/comment.info index f6c17f96a..a6ca0456a 100644 --- a/modules/comment/comment.info +++ b/modules/comment/comment.info @@ -9,8 +9,8 @@ files[] = comment.test configure = admin/content/comment stylesheets[all][] = comment.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/comment/comment.install b/modules/comment/comment.install index 7312e2a31..e4da58f38 100644 --- a/modules/comment/comment.install +++ b/modules/comment/comment.install @@ -344,6 +344,15 @@ function comment_update_7006(&$sandbox) { } } +/** + * @} End of "addtogroup updates-6.x-to-7.x". + */ + +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Add an index to the created column. */ @@ -371,15 +380,6 @@ function comment_update_7008() { db_drop_index('comment', 'nid'); } -/** - * @} End of "addtogroup updates-6.x-to-7.x" - */ - -/** - * @addtogroup updates-7.x-extra - * @{ - */ - /** * Change the last_comment_timestamp column description. */ @@ -393,7 +393,7 @@ function comment_update_7009() { } /** - * @} End of "addtogroup updates-7.x-extra" + * @} End of "addtogroup updates-7.x-extra". */ /** diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 69388337b..429c3b01b 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -103,6 +103,7 @@ function comment_entity_info() { 'id' => 'cid', 'bundle' => 'node_type', 'label' => 'subject', + 'language' => 'language', ), 'bundles' => array(), 'view modes' => array( diff --git a/modules/comment/comment.pages.inc b/modules/comment/comment.pages.inc index 7e88bffcb..482e3f2ff 100644 --- a/modules/comment/comment.pages.inc +++ b/modules/comment/comment.pages.inc @@ -23,8 +23,12 @@ * Some comments are replies to other comments. In those cases, $pid is the parent * comment's cid. * - * @return - * The rendered parent node or comment plus the new comment form. + * @return array + * An associative array containing: + * - An array for rendering the node or parent comment. + * - comment_node: If the comment is a reply to the node. + * - comment_parent: If the comment is a reply to another comment. + * - comment_form: The comment form as a renderable array. */ function comment_reply($node, $pid = NULL) { // Set the breadcrumb trail. diff --git a/modules/comment/comment.test b/modules/comment/comment.test index 4c6755522..e787d25d1 100644 --- a/modules/comment/comment.test +++ b/modules/comment/comment.test @@ -2196,3 +2196,28 @@ class CommentThreadingTestCase extends CommentHelperCase { $this->assertEqual($reply_loaded->thread, '02.01/'); } } + +/** + * Tests that comments behave correctly when the node is changed. + */ +class CommentNodeChangesTestCase extends CommentHelperCase { + + public static function getInfo() { + return array( + 'name' => 'Comment deletion on node changes', + 'description' => 'Tests that comments behave correctly when the node is changed.', + 'group' => 'Comment', + ); + } + + /** + * Tests that comments are deleted with the node. + */ + function testNodeDeletion() { + $this->drupalLogin($this->web_user); + $comment = $this->postComment($this->node, $this->randomName(), $this->randomName()); + $this->assertTrue(comment_load($comment->id), 'The comment could be loaded.'); + node_delete($this->node->nid); + $this->assertFalse(comment_load($comment->id), 'The comment could not be loaded after the node was deleted.'); + } +} diff --git a/modules/contact/contact.info b/modules/contact/contact.info index f43bb6b0a..609f4dcc4 100644 --- a/modules/contact/contact.info +++ b/modules/contact/contact.info @@ -6,8 +6,8 @@ core = 7.x files[] = contact.test configure = admin/structure/contact -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/contact/contact.install b/modules/contact/contact.install index f6015581b..fba5cfd4b 100644 --- a/modules/contact/contact.install +++ b/modules/contact/contact.install @@ -164,5 +164,5 @@ function contact_update_7003() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/contextual/contextual.info b/modules/contextual/contextual.info index 45c71d98c..2a4220758 100644 --- a/modules/contextual/contextual.info +++ b/modules/contextual/contextual.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = contextual.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/dashboard/dashboard.info b/modules/dashboard/dashboard.info index 106f8b124..4218e2c1b 100644 --- a/modules/dashboard/dashboard.info +++ b/modules/dashboard/dashboard.info @@ -7,8 +7,8 @@ files[] = dashboard.test dependencies[] = block configure = admin/dashboard/customize -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/dblog/dblog.info b/modules/dblog/dblog.info index d2bff75a4..359de0db4 100644 --- a/modules/dblog/dblog.info +++ b/modules/dblog/dblog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = dblog.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/dblog/dblog.install b/modules/dblog/dblog.install index 759c7bc1a..1eedcb0d6 100644 --- a/modules/dblog/dblog.install +++ b/modules/dblog/dblog.install @@ -138,5 +138,5 @@ function dblog_update_7001() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/dblog/dblog.test b/modules/dblog/dblog.test index 2e9810b86..a91356c85 100644 --- a/modules/dblog/dblog.test +++ b/modules/dblog/dblog.test @@ -424,7 +424,7 @@ class DBLogTestCase extends DrupalWebTestCase { $severity = WATCHDOG_EMERGENCY; for ($j = 0; $j < 3; $j++) { $types[] = $type = array( - 'count' => mt_rand(1, 5), + 'count' => $j + 1, 'type' => $type_name, 'severity' => $severity++, ); @@ -473,7 +473,7 @@ class DBLogTestCase extends DrupalWebTestCase { $count = $this->getTypeCount($types); $this->assertEqual(array_sum($count), $type['count'], 'Count matched'); } - + // Clear all logs and make sure the confirmation message is found. $this->drupalPost('admin/reports/dblog', array(), t('Clear log messages')); $this->assertText(t('Database log cleared.'), t('Confirmation message found')); diff --git a/modules/field/field.api.php b/modules/field/field.api.php index 134af6615..0d01c59fa 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -676,7 +676,7 @@ function hook_field_is_empty($item, $field) { } /** - * @} End of "defgroup field_types" + * @} End of "defgroup field_types". */ /** @@ -934,6 +934,38 @@ function hook_field_widget_WIDGET_TYPE_form_alter(&$element, &$form_state, $cont $element['#autocomplete_path'] = 'mymodule/autocomplete_path'; } +/** + * Alters the widget properties of a field instance before it gets displayed. + * + * Note that instead of hook_field_widget_properties_alter(), which is called + * for all fields on all entity types, + * hook_field_widget_properties_ENTITY_TYPE_alter() may be used to alter widget + * properties for fields on a specific entity type only. + * + * This hook is called once per field per added or edit entity. If the result + * of the hook involves reading from the database, it is highly recommended to + * statically cache the information. + * + * @param $widget + * The instance's widget properties. + * @param $context + * An associative array containing: + * - entity_type: The entity type; e.g., 'node' or 'user'. + * - entity: The entity object. + * - field: The field that the widget belongs to. + * - instance: The instance of the field. + * + * @see hook_field_widget_properties_ENTITY_TYPE_alter() + */ +function hook_field_widget_properties_alter(&$widget, $context) { + // Change a widget's type according to the time of day. + $field = $context['field']; + if ($context['entity_type'] == 'node' && $field['field_name'] == 'field_foo') { + $time = date('H'); + $widget['type'] = $time < 12 ? 'widget_am' : 'widget_pm'; + } +} + /** * Flag a field-level validation error. * @@ -954,12 +986,12 @@ function hook_field_widget_WIDGET_TYPE_form_alter(&$element, &$form_state, $cont * An associative array containing the current state of the form. */ function hook_field_widget_error($element, $error, $form, &$form_state) { - form_error($element['value'], $error['message']); + form_error($element, $error['message']); } /** - * @} End of "defgroup field_widget" + * @} End of "defgroup field_widget". */ @@ -1198,7 +1230,7 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la } /** - * @} End of "defgroup field_formatter" + * @} End of "defgroup field_formatter". */ /** @@ -1554,11 +1586,11 @@ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) { } /** - * @} End of "defgroup field_attach" + * @} End of "defgroup field_attach". */ /** - * @ingroup field_storage + * @addtogroup field_storage * @{ */ @@ -2326,38 +2358,6 @@ function hook_field_extra_fields_display_alter(&$displays, $context) { } } -/** - * Alters the widget properties of a field instance before it gets displayed. - * - * Note that instead of hook_field_widget_properties_alter(), which is called - * for all fields on all entity types, - * hook_field_widget_properties_ENTITY_TYPE_alter() may be used to alter widget - * properties for fields on a specific entity type only. - * - * This hook is called once per field per added or edit entity. If the result - * of the hook involves reading from the database, it is highly recommended to - * statically cache the information. - * - * @param $widget - * The instance's widget properties. - * @param $context - * An associative array containing: - * - entity_type: The entity type; e.g., 'node' or 'user'. - * - entity: The entity object. - * - field: The field that the widget belongs to. - * - instance: The instance of the field. - * - * @see hook_field_widget_properties_ENTITY_TYPE_alter() - */ -function hook_field_widget_properties_alter(&$widget, $context) { - // Change a widget's type according to the time of day. - $field = $context['field']; - if ($context['entity_type'] == 'node' && $field['field_name'] == 'field_foo') { - $time = date('H'); - $widget['type'] = $time < 12 ? 'widget_am' : 'widget_pm'; - } -} - /** * Alters the widget properties of a field instance on a given entity type * before it gets displayed. @@ -2391,11 +2391,11 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) { } /** - * @} End of "ingroup field_storage" + * @} End of "addtogroup field_storage". */ /** - * @ingroup field_crud + * @addtogroup field_crud * @{ */ @@ -2644,7 +2644,7 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) { } /** - * @} End of "ingroup field_crud" + * @} End of "addtogroup field_crud". */ /** @@ -2675,5 +2675,5 @@ function hook_field_access($op, $field, $entity_type, $entity, $account) { } /** - * @} End of "addtogroup hooks" + * @} End of "addtogroup hooks". */ diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 36117eb7a..868d7bd75 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -65,7 +65,7 @@ define('FIELD_STORAGE_UPDATE', 'update'); define('FIELD_STORAGE_INSERT', 'insert'); /** - * @} End of "defgroup field_storage" + * @} End of "defgroup field_storage". */ /** @@ -1365,5 +1365,5 @@ function field_attach_delete_bundle($entity_type, $bundle) { /** - * @} End of "defgroup field_attach" + * @} End of "defgroup field_attach". */ diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc index 5641375e5..6c27c4329 100644 --- a/modules/field/field.form.inc +++ b/modules/field/field.form.inc @@ -37,90 +37,86 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode, // Collect widget elements. $elements = array(); - if (field_access('edit', $field, $entity_type, $entity)) { - // Store field information in $form_state. - if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) { - $field_state = array( - 'field' => $field, - 'instance' => $instance, - 'items_count' => count($items), - 'array_parents' => array(), - 'errors' => array(), - ); - field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state); - } - // If field module handles multiple values for this form element, and we - // are displaying an individual element, process the multiple value form. - if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { - // Store the entity in the form. - $form['#entity'] = $entity; - $elements = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state); - } - // If the widget is handling multiple values (e.g Options), or if we are - // displaying an individual element, just get a single form element and - // make it the $delta value. - else { - $delta = isset($get_delta) ? $get_delta : 0; - $function = $instance['widget']['module'] . '_field_widget_form'; - if (function_exists($function)) { - $element = array( - '#entity' => $entity, - '#entity_type' => $instance['entity_type'], - '#bundle' => $instance['bundle'], - '#field_name' => $field_name, - '#language' => $langcode, - '#field_parents' => $parents, - '#columns' => array_keys($field['columns']), - '#title' => check_plain($instance['label']), - '#description' => field_filter_xss($instance['description']), - // Only the first widget should be required. - '#required' => $delta == 0 && $instance['required'], - '#delta' => $delta, + // Store field information in $form_state. + if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) { + $field_state = array( + 'field' => $field, + 'instance' => $instance, + 'items_count' => count($items), + 'array_parents' => array(), + 'errors' => array(), + ); + field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state); + } + + // If field module handles multiple values for this form element, and we are + // displaying an individual element, process the multiple value form. + if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { + // Store the entity in the form. + $form['#entity'] = $entity; + $elements = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state); + } + // If the widget is handling multiple values (e.g Options), or if we are + // displaying an individual element, just get a single form element and make + // it the $delta value. + else { + $delta = isset($get_delta) ? $get_delta : 0; + $function = $instance['widget']['module'] . '_field_widget_form'; + if (function_exists($function)) { + $element = array( + '#entity' => $entity, + '#entity_type' => $instance['entity_type'], + '#bundle' => $instance['bundle'], + '#field_name' => $field_name, + '#language' => $langcode, + '#field_parents' => $parents, + '#columns' => array_keys($field['columns']), + '#title' => check_plain($instance['label']), + '#description' => field_filter_xss($instance['description']), + // Only the first widget should be required. + '#required' => $delta == 0 && $instance['required'], + '#delta' => $delta, + ); + if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) { + // Allow modules to alter the field widget form element. + $context = array( + 'form' => $form, + 'field' => $field, + 'instance' => $instance, + 'langcode' => $langcode, + 'items' => $items, + 'delta' => $delta, ); - if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) { - // Allow modules to alter the field widget form element. - $context = array( - 'form' => $form, - 'field' => $field, - 'instance' => $instance, - 'langcode' => $langcode, - 'items' => $items, - 'delta' => $delta, - ); - drupal_alter(array('field_widget_form', 'field_widget_' . $instance['widget']['type'] . '_form'), $element, $form_state, $context); - - // If we're processing a specific delta value for a field where the - // field module handles multiples, set the delta in the result. - // For fields that handle their own processing, we can't make - // assumptions about how the field is structured, just merge in the - // returned element. - if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { - $elements[$delta] = $element; - } - else { - $elements = $element; - } + drupal_alter(array('field_widget_form', 'field_widget_' . $instance['widget']['type'] . '_form'), $element, $form_state, $context); + + // If we're processing a specific delta value for a field where the + // field module handles multiples, set the delta in the result. + // For fields that handle their own processing, we can't make + // assumptions about how the field is structured, just merge in the + // returned element. + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { + $elements[$delta] = $element; + } + else { + $elements = $element; } } } } - if ($elements) { - // Also aid in theming of field widgets by rendering a classified - // container. - $addition[$field_name] = array( - '#type' => 'container', - '#attributes' => array( - 'class' => array( - 'field-type-' . drupal_html_class($field['type']), - 'field-name-' . drupal_html_class($field_name), - 'field-widget-' . drupal_html_class($instance['widget']['type']), - ), + // Also aid in theming of field widgets by rendering a classified container. + $addition[$field_name] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'field-type-' . drupal_html_class($field['type']), + 'field-name-' . drupal_html_class($field_name), + 'field-widget-' . drupal_html_class($instance['widget']['type']), ), - '#weight' => $instance['widget']['weight'], - ); - } + ), + '#weight' => $instance['widget']['weight'], + ); // Populate the 'array_parents' information in $form_state['field'] after // the form is built, so that we catch changes in the form structure performed @@ -136,6 +132,7 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode, // when $langcode is unknown. '#language' => $langcode, $langcode => $elements, + '#access' => field_access('edit', $field, $entity_type, $entity), ); return $addition; @@ -362,31 +359,33 @@ function field_default_form_errors($entity_type, $entity, $field, $instance, $la $field_state = field_form_get_state($form['#parents'], $field['field_name'], $langcode, $form_state); if (!empty($field_state['errors'])) { - $function = $instance['widget']['module'] . '_field_widget_error'; - $function_exists = function_exists($function); - - // Locate the correct element in the the form. + // Locate the correct element in the form. $element = drupal_array_get_nested_value($form_state['complete form'], $field_state['array_parents']); - - $multiple_widget = field_behaviors_widget('multiple values', $instance) != FIELD_BEHAVIOR_DEFAULT; - foreach ($field_state['errors'] as $delta => $delta_errors) { - // For multiple single-value widgets, pass errors by delta. - // For a multiple-value widget, all errors are passed to the main widget. - $error_element = $multiple_widget ? $element : $element[$delta]; - foreach ($delta_errors as $error) { - if ($function_exists) { - $function($error_element, $error, $form, $form_state); - } - else { - // Make sure that errors are reported (even incorrectly flagged) if - // the widget module fails to implement hook_field_widget_error(). - form_error($error_element, $error['error']); + // Only set errors if the element is accessible. + if (!isset($element['#access']) || $element['#access']) { + $function = $instance['widget']['module'] . '_field_widget_error'; + $function_exists = function_exists($function); + + $multiple_widget = field_behaviors_widget('multiple values', $instance) != FIELD_BEHAVIOR_DEFAULT; + foreach ($field_state['errors'] as $delta => $delta_errors) { + // For multiple single-value widgets, pass errors by delta. + // For a multiple-value widget, pass all errors to the main widget. + $error_element = $multiple_widget ? $element : $element[$delta]; + foreach ($delta_errors as $error) { + if ($function_exists) { + $function($error_element, $error, $form, $form_state); + } + else { + // Make sure that errors are reported (even incorrectly flagged) if + // the widget module fails to implement hook_field_widget_error(). + form_error($error_element, $error['message']); + } } } + // Reinitialize the errors list for the next submit. + $field_state['errors'] = array(); + field_form_set_state($form['#parents'], $field['field_name'], $langcode, $form_state, $field_state); } - // Reinitialize the errors list for the next submit. - $field_state['errors'] = array(); - field_form_set_state($form['#parents'], $field['field_name'], $langcode, $form_state, $field_state); } } diff --git a/modules/field/field.info b/modules/field/field.info index 9ff281132..b950f2e44 100644 --- a/modules/field/field.info +++ b/modules/field/field.info @@ -10,8 +10,8 @@ dependencies[] = field_sql_storage required = TRUE stylesheets[all][] = theme/field.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index e7eaaf0c6..eb5cc5ca7 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -613,7 +613,8 @@ function field_info_fields() { * @return * The field array, as returned by field_read_fields(), with an * additional element 'bundles', whose value is an array of all the bundles - * this field belongs to keyed by entity type. + * this field belongs to keyed by entity type. NULL if the field was not + * found. * * @see field_info_field_by_id() */ @@ -890,5 +891,5 @@ function field_info_storage_settings($type) { } /** - * @} End of "defgroup field_info" + * @} End of "defgroup field_info". */ diff --git a/modules/field/field.install b/modules/field/field.install index dff3949fb..5934a264c 100644 --- a/modules/field/field.install +++ b/modules/field/field.install @@ -434,7 +434,7 @@ function field_update_7001() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ /** @@ -458,5 +458,5 @@ function field_update_7002() { } /** - * @} End of "addtogroup updates-7.x-extra" + * @} End of "addtogroup updates-7.x-extra". */ diff --git a/modules/field/field.module b/modules/field/field.module index dedf8470c..6fc97a2bf 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -423,13 +423,9 @@ function field_modules_disabled($modules) { function field_sync_field_status() { // Refresh the 'active' and 'storage_active' columns according to the current // set of enabled modules. - $all_modules = system_rebuild_module_data(); - $modules = array(); - foreach ($all_modules as $module_name => $module) { - if ($module->status) { - $modules[] = $module_name; - field_associate_fields($module_name); - } + $modules = module_list(); + foreach ($modules as $module_name) { + field_associate_fields($module_name); } db_update('field_config') ->fields(array('active' => 0)) @@ -555,51 +551,33 @@ function _field_sort_items_value_helper($a, $b) { /** * Gets or sets administratively defined bundle settings. * - * For each bundle, settings are provided as a nested array with the following - * structure: - * @code - * array( - * 'view_modes' => array( - * // One sub-array per view mode for the entity type: - * 'full' => array( - * 'custom_display' => Whether the view mode uses custom display - * settings or settings of the 'default' mode, - * ), - * 'teaser' => ... - * ), - * 'extra_fields' => array( - * 'form' => array( - * // One sub-array per pseudo-field in displayed entities: - * 'extra_field_1' => array( - * 'weight' => The weight of the pseudo-field, - * ), - * 'extra_field_2' => ... - * ), - * 'display' => array( - * // One sub-array per pseudo-field in displayed entities: - * 'extra_field_1' => array( - * // One sub-array per view mode for the entity type, including - * // the 'default' mode: - * 'default' => array( - * 'weight' => The weight of the pseudo-field, - * 'visible' => TRUE if the pseudo-field is visible, FALSE if hidden, - * ), - * 'full' => ... - * ), - * 'extra_field_2' => ... - * ), - * ), - * ); - * @endcode - * - * @param $entity_type + * @param string $entity_type * The type of $entity; e.g., 'node' or 'user'. - * @param $bundle + * @param string $bundle * The bundle name. - * @param $settings - * (optional) The settings to store. + * @param array|null $settings + * (optional) The settings to store, an associative array with the following + * elements: + * - view_modes: An associative array keyed by view mode, with the following + * key/value pairs: + * - custom_settings: Boolean specifying whether the view mode uses a + * dedicated set of display options (TRUE), or the 'default' options + * (FALSE). Defaults to FALSE. + * - extra_fields: An associative array containing the form and display + * settings for extra fields (also known as pseudo-fields): + * - form: An associative array whose keys are the names of extra fields, + * and whose values are associative arrays with the following elements: + * - weight: The weight of the extra field, determining its position on an + * entity form. + * - display: An associative array whose keys are the names of extra fields, + * and whose values are associative arrays keyed by the name of view + * modes. This array must include an item for the 'default' view mode. + * Each view mode sub-array contains the following elements: + * - weight: The weight of the extra field, determining its position when + * an entity is viewed. + * - visible: TRUE if the extra field is visible, FALSE otherwise. * - * @return + * @return array|null * If no $settings are passed, the current settings are returned. */ function field_bundle_settings($entity_type, $bundle, $settings = NULL) { @@ -972,6 +950,10 @@ function field_has_data($field) { ->fieldCondition($field) ->range(0, 1) ->count() + // Neutralize the 'entity_field_access' query tag added by + // field_sql_storage_field_storage_query(). The result cannot depend on the + // access grants of the current user. + ->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT') ->execute(); } @@ -1112,7 +1094,7 @@ function template_process_field(&$variables, $hook) { } } /** - * @} End of "defgroup field" + * @} End of "defgroup field". */ /** diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.info b/modules/field/modules/field_sql_storage/field_sql_storage.info index 532cb8381..125b4a51e 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.info +++ b/modules/field/modules/field_sql_storage/field_sql_storage.info @@ -7,8 +7,8 @@ dependencies[] = field files[] = field_sql_storage.test required = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.install b/modules/field/modules/field_sql_storage/field_sql_storage.install index 647296e4e..78c520fcf 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.install +++ b/modules/field/modules/field_sql_storage/field_sql_storage.install @@ -211,5 +211,5 @@ function field_sql_storage_update_7002() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.module b/modules/field/modules/field_sql_storage/field_sql_storage.module index 2ed783507..a75619427 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.module +++ b/modules/field/modules/field_sql_storage/field_sql_storage.module @@ -512,7 +512,12 @@ function field_sql_storage_field_storage_query(EntityFieldQuery $query) { } else { $select_query = db_select($tablename, $table_alias); - $select_query->addTag('entity_field_access'); + // Allow queries internal to the Field API to opt out of the access + // check, for situations where the query's results should not depend on + // the access grants for the current user. + if (!isset($query->tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) { + $select_query->addTag('entity_field_access'); + } $select_query->addMetaData('base_table', $tablename); $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle')); $field_base_table = $table_alias; diff --git a/modules/field/modules/list/list.info b/modules/field/modules/list/list.info index d3f92ab85..43300c261 100644 --- a/modules/field/modules/list/list.info +++ b/modules/field/modules/list/list.info @@ -7,8 +7,8 @@ dependencies[] = field dependencies[] = options files[] = tests/list.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/modules/list/list.install b/modules/field/modules/list/list.install index 91c7649c1..2386f0483 100644 --- a/modules/field/modules/list/list.install +++ b/modules/field/modules/list/list.install @@ -116,6 +116,11 @@ function _list_update_7001_extract_allowed_values($string, $position_keys) { return $values; } +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Re-apply list_update_7001() for deleted fields. */ @@ -126,4 +131,8 @@ function list_update_7002() { // list_update_7001() has the required checks to ensure it is reentrant, so // it can simply be executed once more.. list_update_7001(); -} \ No newline at end of file +} + +/** + * @} End of "addtogroup updates-7.x-extra". + */ diff --git a/modules/field/modules/list/tests/list_test.info b/modules/field/modules/list/tests/list_test.info index b524e183d..666ab1e7e 100644 --- a/modules/field/modules/list/tests/list_test.info +++ b/modules/field/modules/list/tests/list_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/modules/number/number.info b/modules/field/modules/number/number.info index ed98de241..261376831 100644 --- a/modules/field/modules/number/number.info +++ b/modules/field/modules/number/number.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = number.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/modules/options/options.info b/modules/field/modules/options/options.info index 65e620b85..ef5c87b12 100644 --- a/modules/field/modules/options/options.info +++ b/modules/field/modules/options/options.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = options.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/modules/text/text.info b/modules/field/modules/text/text.info index 6e1aba775..7c21f9bb2 100644 --- a/modules/field/modules/text/text.info +++ b/modules/field/modules/text/text.info @@ -7,8 +7,8 @@ dependencies[] = field files[] = text.test required = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index f7d9dddb2..739ddbe99 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -1650,6 +1650,18 @@ class FieldFormTestCase extends FieldTestCase { $langcode = LANGUAGE_NONE; + // Test that the form structure includes full information for each delta + // apart from #access. + $entity_type = 'test_entity'; + $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']); + + $form = array(); + $form_state = form_state_defaults(); + field_attach_form($entity_type, $entity, $form, $form_state); + + $this->assertEqual($form[$field_name_no_access][$langcode][0]['value']['#entity_type'], $entity_type, 'The correct entity type is set in the field structure.'); + $this->assertFalse($form[$field_name_no_access]['#access'], 'Field #access is FALSE for the field without edit access.'); + // Display creation form. $this->drupalGet('test-entity/add/test-bundle'); $this->assertNoFieldByName("{$field_name_no_access}[$langcode][0][value]", '', t('Widget is not displayed if field access is denied.')); @@ -2794,7 +2806,7 @@ class FieldTranslationsTestCase extends FieldTestCase { $options = array(); $entities = array(); $entity_type = 'test_entity'; - $entity_count = mt_rand(2, 5); + $entity_count = 5; $available_languages = field_available_languages($this->entity_type, $this->field); for ($id = 1; $id <= $entity_count; ++$id) { diff --git a/modules/field/tests/field_test.info b/modules/field/tests/field_test.info index 0262b5b95..75a84caaa 100644 --- a/modules/field/tests/field_test.info +++ b/modules/field/tests/field_test.info @@ -6,8 +6,8 @@ files[] = field_test.entity.inc version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field/tests/field_test.module b/modules/field/tests/field_test.module index 0015cd905..37ea7b1dd 100644 --- a/modules/field/tests/field_test.module +++ b/modules/field/tests/field_test.module @@ -259,3 +259,14 @@ function field_test_field_widget_form_alter(&$element, &$form_state, $context) { break; } } + +/** + * Implements hook_query_TAG_alter() for tag 'efq_table_prefixing_test'. + * + * @see EntityFieldQueryTestCase::testTablePrefixing() + */ +function field_test_query_efq_table_prefixing_test_alter(&$query) { + // Add an additional join onto the entity base table. This will cause an + // exception if the EFQ does not properly prefix the base table. + $query->join('test_entity','te2','%alias.ftid = test_entity.ftid'); +} diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc index 33ce2f05c..44770acb9 100644 --- a/modules/field_ui/field_ui.admin.inc +++ b/modules/field_ui/field_ui.admin.inc @@ -162,7 +162,8 @@ function field_ui_table_pre_render($elements) { // Add tabledrag indentation to the first row cell. if ($depth = count($parents[$name])) { - $cell = current(element_children($row)); + $children = element_children($row); + $cell = current($children); $row[$cell]['#prefix'] = theme('indentation', array('size' => $depth)) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : ''); } @@ -317,7 +318,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle t('Weight'), t('Parent'), t('Machine name'), - t('Field'), + t('Field type'), t('Widget'), array('data' => t('Operations'), 'colspan' => 2), ), diff --git a/modules/field_ui/field_ui.api.php b/modules/field_ui/field_ui.api.php index 882f467a6..05d9f053f 100644 --- a/modules/field_ui/field_ui.api.php +++ b/modules/field_ui/field_ui.api.php @@ -200,5 +200,5 @@ function hook_field_formatter_settings_summary($field, $instance, $view_mode) { } /** - * @} End of "addtogroup field_types" + * @} End of "addtogroup field_types". */ diff --git a/modules/field_ui/field_ui.info b/modules/field_ui/field_ui.info index 10760760a..8757a5299 100644 --- a/modules/field_ui/field_ui.info +++ b/modules/field_ui/field_ui.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = field_ui.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/field_ui/field_ui.js b/modules/field_ui/field_ui.js index 1017937b6..65b28d049 100644 --- a/modules/field_ui/field_ui.js +++ b/modules/field_ui/field_ui.js @@ -97,7 +97,7 @@ jQuery.fn.fieldUIPopulateOptions = function (options, selected) { html += '<option value="' + value + '"' + (is_selected ? ' selected="selected"' : '') + '>' + text + '</option>'; }); - $(this).html(html).attr('disabled', disabled ? 'disabled' : ''); + $(this).html(html).attr('disabled', disabled ? 'disabled' : false); }); }; diff --git a/modules/field_ui/field_ui.test b/modules/field_ui/field_ui.test index 75a70846f..d0a822a82 100644 --- a/modules/field_ui/field_ui.test +++ b/modules/field_ui/field_ui.test @@ -173,7 +173,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { $table_headers = array( t('Label'), t('Machine name'), - t('Field'), + t('Field type'), t('Widget'), t('Operations'), ); diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index 7a5697ccb..1189704fd 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -261,11 +261,8 @@ function file_field_update($entity_type, $entity, $field, $instance, $langcode, $current_fids[] = $item['fid']; } - // Create a bare-bones entity so that we can load its previous values. - $original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle)); - field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id'])); - // Compare the original field values with the ones that are being saved. + $original = $entity->original; $original_fids = array(); if (!empty($original->{$field['field_name']}[$langcode])) { foreach ($original->{$field['field_name']}[$langcode] as $original_item) { @@ -630,10 +627,9 @@ function file_field_widget_process($element, &$form_state, $form) { // Add the description field if enabled. if (!empty($instance['settings']['description_field']) && $item['fid']) { $element['description'] = array( - '#type' => 'textfield', + '#type' => variable_get('file_description_type', 'textfield'), '#title' => t('Description'), '#value' => isset($item['description']) ? $item['description'] : '', - '#type' => variable_get('file_description_type', 'textfield'), '#maxlength' => variable_get('file_description_length', 128), '#description' => t('The description may be used as the label of the link to the file.'), ); diff --git a/modules/file/file.info b/modules/file/file.info index bf896788c..99b08f96d 100644 --- a/modules/file/file.info +++ b/modules/file/file.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = tests/file.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/file/file.js b/modules/file/file.js index 1a9f87e4f..577480bbc 100644 --- a/modules/file/file.js +++ b/modules/file/file.js @@ -110,7 +110,7 @@ Drupal.file = Drupal.file || { var $fieldsToTemporarilyDisable = $('div.form-managed-file input.form-file').not($enabledFields).not(':disabled'); $fieldsToTemporarilyDisable.attr('disabled', 'disabled'); setTimeout(function (){ - $fieldsToTemporarilyDisable.attr('disabled', ''); + $fieldsToTemporarilyDisable.attr('disabled', false); }, 1000); }, /** diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index 7fa6d6f01..76708a3c7 100644 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -551,7 +551,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { // Remove access comments permission from anon user. $edit = array( - '1[access comments]' => FALSE, + DRUPAL_ANONYMOUS_RID . '[access comments]' => FALSE, ); $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); diff --git a/modules/file/tests/file_module_test.info b/modules/file/tests/file_module_test.info index a7a6f55b3..7c1f6e62d 100644 --- a/modules/file/tests/file_module_test.info +++ b/modules/file/tests/file_module_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/filter/filter.info b/modules/filter/filter.info index a4823170a..70af4048f 100644 --- a/modules/filter/filter.info +++ b/modules/filter/filter.info @@ -7,8 +7,8 @@ files[] = filter.test required = TRUE configure = admin/config/content/formats -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/filter/filter.install b/modules/filter/filter.install index 19fd8aa18..9d17eb54b 100644 --- a/modules/filter/filter.install +++ b/modules/filter/filter.install @@ -490,5 +490,5 @@ function filter_update_7010() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/filter/filter.test b/modules/filter/filter.test index 1a21ecbbd..aa1693fba 100644 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -314,7 +314,7 @@ class FilterAdminTestCase extends DrupalWebTestCase { $edit = array(); $edit['format'] = drupal_strtolower($this->randomName()); $edit['name'] = $this->randomName(); - $edit['roles[2]'] = 1; + $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = 1; $edit['filters[' . $second_filter . '][status]'] = TRUE; $edit['filters[' . $first_filter . '][status]'] = TRUE; $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); @@ -324,7 +324,7 @@ class FilterAdminTestCase extends DrupalWebTestCase { $format = filter_format_load($edit['format']); $this->assertNotNull($format, t('Format found in database.')); - $this->assertFieldByName('roles[2]', '', t('Role found.')); + $this->assertFieldByName('roles[' . DRUPAL_AUTHENTICATED_RID . ']', '', t('Role found.')); $this->assertFieldByName('filters[' . $second_filter . '][status]', '', t('Line break filter found.')); $this->assertFieldByName('filters[' . $first_filter . '][status]', '', t('Url filter found.')); @@ -335,8 +335,8 @@ class FilterAdminTestCase extends DrupalWebTestCase { // Allow authenticated users on full HTML. $format = filter_format_load($full); $edit = array(); - $edit['roles[1]'] = 0; - $edit['roles[2]'] = 1; + $edit['roles[' . DRUPAL_ANONYMOUS_RID . ']'] = 0; + $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = 1; $this->drupalPost('admin/config/content/formats/' . $full, $edit, t('Save configuration')); $this->assertRaw(t('The text format %format has been updated.', array('%format' => $format->name)), t('Full HTML format successfully updated.')); @@ -386,10 +386,10 @@ class FilterAdminTestCase extends DrupalWebTestCase { // Full HTML. $edit = array(); - $edit['roles[2]'] = FALSE; + $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = FALSE; $this->drupalPost('admin/config/content/formats/' . $full, $edit, t('Save configuration')); $this->assertRaw(t('The text format %format has been updated.', array('%format' => $format->name)), t('Full HTML format successfully reverted.')); - $this->assertFieldByName('roles[2]', $edit['roles[2]'], t('Changes reverted.')); + $this->assertFieldByName('roles[' . DRUPAL_AUTHENTICATED_RID . ']', $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'], t('Changes reverted.')); // Filter order. $edit = array(); @@ -1195,6 +1195,11 @@ class FilterUnitTestCase extends DrupalUnitTestCase { // - absolute, mail, partial // - characters/encoding, surrounding markup, security + // Create a e-mail that is too long. + $long_email = str_repeat('a', 254) . '@example.com'; + $too_long_email = str_repeat('b', 255) . '@example.com'; + + // Filter selection/pattern matching. $tests = array( // HTTP URLs. @@ -1206,10 +1211,12 @@ http://example.com or www.example.com ), // MAILTO URLs. ' -person@example.com or mailto:person2@example.com +person@example.com or mailto:person2@example.com or ' . $long_email . ' but not ' . $too_long_email . ' ' => array( '<a href="mailto:person@example.com">person@example.com</a>' => TRUE, '<a href="mailto:person2@example.com">mailto:person2@example.com</a>' => TRUE, + '<a href="mailto:' . $long_email . '">' . $long_email . '</a>' => TRUE, + '<a href="mailto:' . $too_long_email . '">' . $too_long_email . '</a>' => FALSE, ), // URI parts and special characters. ' @@ -1800,7 +1807,7 @@ class FilterHooksTestCase extends DrupalWebTestCase { $edit = array(); $edit['format'] = drupal_strtolower($this->randomName()); $edit['name'] = $name; - $edit['roles[1]'] = 1; + $edit['roles[' . DRUPAL_ANONYMOUS_RID . ']'] = 1; $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); $this->assertRaw(t('Added text format %format.', array('%format' => $name)), t('New format created.')); $this->assertText('hook_filter_format_insert invoked.', t('hook_filter_format_insert was invoked.')); @@ -1809,7 +1816,7 @@ class FilterHooksTestCase extends DrupalWebTestCase { // Update text format. $edit = array(); - $edit['roles[2]'] = 1; + $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'] = 1; $this->drupalPost('admin/config/content/formats/' . $format_id, $edit, t('Save configuration')); $this->assertRaw(t('The text format %format has been updated.', array('%format' => $name)), t('Format successfully updated.')); $this->assertText('hook_filter_format_update invoked.', t('hook_filter_format_update() was invoked.')); diff --git a/modules/forum/forum-icon.tpl.php b/modules/forum/forum-icon.tpl.php index 9cf2cd8d3..fd1cd1308 100644 --- a/modules/forum/forum-icon.tpl.php +++ b/modules/forum/forum-icon.tpl.php @@ -2,7 +2,7 @@ /** * @file - * Default theme implementation to display an appropriate icon for a forum post. + * Displays an appropriate icon for a forum post. * * Available variables: * - $new_posts: Indicates whether or not the topic contains new posts. @@ -12,6 +12,8 @@ * * @see template_preprocess_forum_icon() * @see theme_forum_icon() + * + * @ingroup themeable */ ?> <div class="topic-status-<?php print $icon_class ?>" title="<?php print $icon_title ?>"> diff --git a/modules/forum/forum-list.tpl.php b/modules/forum/forum-list.tpl.php index 257cea947..01c74a34c 100644 --- a/modules/forum/forum-list.tpl.php +++ b/modules/forum/forum-list.tpl.php @@ -2,34 +2,35 @@ /** * @file - * Default theme implementation to display a list of forums and containers. + * Displays a list of forums and containers. * * Available variables: * - $forums: An array of forums and containers to display. It is keyed to the - * numeric id's of all child forums and containers. - * - $forum_id: Forum id for the current forum. Parent to all items within - * the $forums array. - * - * Each $forum in $forums contains: - * - $forum->is_container: Is TRUE if the forum can contain other forums. Is - * FALSE if the forum can contain only topics. - * - $forum->depth: How deep the forum is in the current hierarchy. - * - $forum->zebra: 'even' or 'odd' string used for row class. - * - $forum->icon_class: 'default' or 'new' string used for forum icon class. - * - $forum->icon_title: Text alternative for the forum icon. - * - $forum->name: The name of the forum. - * - $forum->link: The URL to link to this forum. - * - $forum->description: The description of this forum. - * - $forum->new_topics: True if the forum contains unread posts. - * - $forum->new_url: A URL to the forum's unread posts. - * - $forum->new_text: Text for the above URL which tells how many new posts. - * - $forum->old_topics: A count of posts that have already been read. - * - $forum->num_posts: The total number of posts in the forum. - * - $forum->last_reply: Text representing the last time a forum was posted or - * commented in. + * numeric IDs of all child forums and containers. Each $forum in $forums + * contains: + * - $forum->is_container: TRUE if the forum can contain other forums. FALSE + * if the forum can contain only topics. + * - $forum->depth: How deep the forum is in the current hierarchy. + * - $forum->zebra: 'even' or 'odd' string used for row class. + * - $forum->icon_class: 'default' or 'new' string used for forum icon class. + * - $forum->icon_title: Text alternative for the forum icon. + * - $forum->name: The name of the forum. + * - $forum->link: The URL to link to this forum. + * - $forum->description: The description of this forum. + * - $forum->new_topics: TRUE if the forum contains unread posts. + * - $forum->new_url: A URL to the forum's unread posts. + * - $forum->new_text: Text for the above URL, which tells how many new posts. + * - $forum->old_topics: A count of posts that have already been read. + * - $forum->num_posts: The total number of posts in the forum. + * - $forum->last_reply: Text representing the last time a forum was posted or + * commented in. + * - $forum_id: Forum ID for the current forum. Parent to all items within the + * $forums array. * * @see template_preprocess_forum_list() * @see theme_forum_list() + * + * @ingroup themeable */ ?> <table id="forum-<?php print $forum_id; ?>"> diff --git a/modules/forum/forum-rtl.css b/modules/forum/forum-rtl.css index 81dd4d396..b475e4286 100644 --- a/modules/forum/forum-rtl.css +++ b/modules/forum/forum-rtl.css @@ -1,3 +1,7 @@ +/** + * @file + * Right-to-left styling for the Forum module. + */ #forum td.forum .icon { float: right; diff --git a/modules/forum/forum-submitted.tpl.php b/modules/forum/forum-submitted.tpl.php index d310448c7..18fea8f1b 100644 --- a/modules/forum/forum-submitted.tpl.php +++ b/modules/forum/forum-submitted.tpl.php @@ -2,18 +2,20 @@ /** * @file - * Default theme implementation to format a simple string indicated when and - * by whom a topic was submitted. + * Formats a forum post submission string. * - * Available variables: + * The submission string indicates when and by whom a topic was submitted. * + * Available variables: * - $author: The author of the post. * - $time: How long ago the post was created. - * - $topic: An object with the raw data of the post. Unsafe, be sure - * to clean this data before printing. + * - $topic: An object with the raw data of the post. Potentially unsafe. Be + * sure to clean this data before printing. * * @see template_preprocess_forum_submitted() * @see theme_forum_submitted() + * + * @ingroup themeable */ ?> <?php if ($time): ?> diff --git a/modules/forum/forum-topic-list.tpl.php b/modules/forum/forum-topic-list.tpl.php index 33907036f..64278141e 100644 --- a/modules/forum/forum-topic-list.tpl.php +++ b/modules/forum/forum-topic-list.tpl.php @@ -2,35 +2,39 @@ /** * @file - * Default theme implementation to display a list of forum topics. + * Displays a list of forum topics. * * Available variables: * - $header: The table header. This is pre-generated with click-sorting * information. If you need to change this, see * template_preprocess_forum_topic_list(). * - $pager: The pager to display beneath the table. - * - $topics: An array of topics to be displayed. - * - $topic_id: Numeric id for the current forum topic. - * - * Each $topic in $topics contains: - * - $topic->icon: The icon to display. - * - $topic->moved: A flag to indicate whether the topic has been moved to - * another forum. - * - $topic->title: The title of the topic. Safe to output. - * - $topic->message: If the topic has been moved, this contains an - * explanation and a link. - * - $topic->zebra: 'even' or 'odd' string used for row class. - * - $topic->comment_count: The number of replies on this topic. - * - $topic->new_replies: A flag to indicate whether there are unread comments. - * - $topic->new_url: If there are unread replies, this is a link to them. - * - $topic->new_text: Text containing the translated, properly pluralized count. - * - $topic->created: An outputtable string represented when the topic was posted. - * - $topic->last_reply: An outputtable string representing when the topic was - * last replied to. - * - $topic->timestamp: The raw timestamp this topic was posted. + * - $topics: An array of topics to be displayed. Each $topic in $topics + * contains: + * - $topic->icon: The icon to display. + * - $topic->moved: A flag to indicate whether the topic has been moved to + * another forum. + * - $topic->title: The title of the topic. Safe to output. + * - $topic->message: If the topic has been moved, this contains an + * explanation and a link. + * - $topic->zebra: 'even' or 'odd' string used for row class. + * - $topic->comment_count: The number of replies on this topic. + * - $topic->new_replies: A flag to indicate whether there are unread + * comments. + * - $topic->new_url: If there are unread replies, this is a link to them. + * - $topic->new_text: Text containing the translated, properly pluralized + * count. + * - $topic->created: A string representing when the topic was posted. Safe + * to output. + * - $topic->last_reply: An outputtable string representing when the topic was + * last replied to. + * - $topic->timestamp: The raw timestamp this topic was posted. + * - $topic_id: Numeric ID for the current forum topic. * * @see template_preprocess_forum_topic_list() * @see theme_forum_topic_list() + * + * @ingroup themeable */ ?> <table id="forum-topic-<?php print $topic_id; ?>"> diff --git a/modules/forum/forum.admin.inc b/modules/forum/forum.admin.inc index 49c71d90a..712cf546e 100644 --- a/modules/forum/forum.admin.inc +++ b/modules/forum/forum.admin.inc @@ -2,7 +2,22 @@ /** * @file - * Administrative page callbacks for the forum module. + * Administrative page callbacks for the Forum module. + */ + +/** + * Page callback: Returns a form for creating a new forum or container. + * + * @param $type + * What is being added. Possible values are 'forum' and 'container'. + * @param $edit + * (optional) Associative array containing a forum term to be edited. + * Defaults to an empty array. + * + * @return + * A form for creating a new forum or container. + * + * @see forum_menu() */ function forum_form_main($type, $edit = array()) { $edit = (array) $edit; @@ -20,11 +35,14 @@ function forum_form_main($type, $edit = array()) { } /** - * Returns a form for adding a forum to the forum vocabulary + * Form constructor for adding and editing a forum. + * + * @param $edit + * (optional) Associative array containing a forum term to be added or edited. + * Defaults to an empty array. * - * @param $edit Associative array containing a forum term to be added or edited. - * @ingroup forms * @see forum_form_submit() + * @ingroup forms */ function forum_form_forum($form, &$form_state, $edit = array()) { $edit += array( @@ -67,7 +85,7 @@ function forum_form_forum($form, &$form_state, $edit = array()) { } /** - * Process forum form and container form submissions. + * Form submission handler for forum_form_forum() and forum_form_container(). */ function forum_form_submit($form, &$form_state) { if ($form['form_id']['#value'] == 'forum_form_container') { @@ -104,8 +122,8 @@ function forum_form_submit($form, &$form_state) { /** * Returns HTML for a forum form. * - * By default this does not alter the appearance of a form at all, - * but is provided as a convenience for themers. + * By default this does not alter the appearance of a form at all, but is + * provided as a convenience for themers. * * @param $variables * An associative array containing: @@ -118,11 +136,14 @@ function theme_forum_form($variables) { } /** - * Returns a form for adding a container to the forum vocabulary + * Form constructor for adding and editing forum containers. + * + * @param $edit + * (optional) Associative array containing a container term to be added or edited. + * Defaults to an empty array. * - * @param $edit Associative array containing a container term to be added or edited. - * @ingroup forms * @see forum_form_submit() + * @ingroup forms */ function forum_form_container($form, &$form_state, $edit = array()) { $edit += array( @@ -176,9 +197,13 @@ function forum_form_container($form, &$form_state, $edit = array()) { } /** - * Returns a confirmation page for deleting a forum taxonomy term. + * Form constructor for confirming deletion of a forum taxonomy term. + * + * @param $tid + * ID of the term to be deleted. * - * @param $tid ID of the term to be deleted + * @see forum_confirm_delete_submit() + * @ingroup forms */ function forum_confirm_delete($form, &$form_state, $tid) { $term = taxonomy_term_load($tid); @@ -190,7 +215,7 @@ function forum_confirm_delete($form, &$form_state, $tid) { } /** - * Implement forms api _submit call. Deletes a forum after confirmation. + * Form submission handler for forum_confirm_delete(). */ function forum_confirm_delete_submit($form, &$form_state) { taxonomy_term_delete($form_state['values']['tid']); @@ -202,9 +227,11 @@ function forum_confirm_delete_submit($form, &$form_state) { } /** - * Form builder for the forum settings page. + * Form constructor for the forum settings page. * + * @see forum_menu() * @see system_settings_form() + * @ingroup forms */ function forum_admin_settings($form) { $number = drupal_map_assoc(array(5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80, 100, 150, 200, 250, 300, 350, 400, 500)); @@ -232,7 +259,13 @@ function forum_admin_settings($form) { } /** - * Returns an overview list of existing forums and containers + * Form constructor for the forum overview form. + * + * Returns a form for controlling the hierarchy of existing forums and + * containers. + * + * @see forum_menu() + * @ingroup forms */ function forum_overview($form, &$form_state) { module_load_include('inc', 'taxonomy', 'taxonomy.admin'); @@ -267,11 +300,17 @@ function forum_overview($form, &$form_state) { } /** - * Returns a select box for available parent terms + * Returns a select box for available parent terms. + * + * @param $tid + * ID of the term that is being added or edited. + * @param $title + * Title for the select box. + * @param $child_type + * Whether the child is a forum or a container. * - * @param $tid ID of the term which is being added or edited - * @param $title Title to display the select box with - * @param $child_type Whether the child is forum or container + * @return + * A select form element. */ function _forum_parent_select($tid, $title, $child_type) { diff --git a/modules/forum/forum.css b/modules/forum/forum.css index 4a67c8bcd..a758bc666 100644 --- a/modules/forum/forum.css +++ b/modules/forum/forum.css @@ -1,3 +1,7 @@ +/** + * @file + * Styling for the Forum module. + */ #forum .description { font-size: 0.9em; diff --git a/modules/forum/forum.info b/modules/forum/forum.info index e05cd92b4..26af81d69 100644 --- a/modules/forum/forum.info +++ b/modules/forum/forum.info @@ -9,8 +9,8 @@ files[] = forum.test configure = admin/structure/forum stylesheets[all][] = forum.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/forum/forum.install b/modules/forum/forum.install index 32a9bb90d..57e116b71 100644 --- a/modules/forum/forum.install +++ b/modules/forum/forum.install @@ -2,7 +2,7 @@ /** * @file - * Install, update and uninstall functions for the forum module. + * Install, update, and uninstall functions for the Forum module. */ /** @@ -219,6 +219,8 @@ function forum_schema() { ), 'indexes' => array( 'forum_topics' => array('nid', 'tid', 'sticky', 'last_comment_timestamp'), + 'created' => array('created'), + 'last_comment_timestamp' => array('last_comment_timestamp'), ), 'foreign keys' => array( 'tracked_node' => array( @@ -349,6 +351,11 @@ function forum_update_7001() { ->execute(); } +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Add new index to forum_index table. */ @@ -357,11 +364,6 @@ function forum_update_7002() { db_add_index('forum_index', 'forum_topics', array('nid', 'tid', 'sticky', 'last_comment_timestamp')); } -/** - * @addtogroup updates-7.x-extra - * @{ - */ - /** * Rename field to 'taxonomy_forums'. */ @@ -440,11 +442,7 @@ function forum_update_7003() { } /** - * @} End of "addtogroup updates-7.x-extra" - */ - -/** - * Update {form_index} so that only published nodes are indexed. + * Update {forum_index} so that only published nodes are indexed. */ function forum_update_7011() { $select = db_select('node', 'n') @@ -455,3 +453,15 @@ function forum_update_7011() { ->condition('nid', $select, 'IN') ->execute(); } + +/** + * Add 'created' and 'last_comment_timestamp' indexes. + */ +function forum_update_7012() { + db_add_index('forum_index', 'created', array('created')); + db_add_index('forum_index', 'last_comment_timestamp', array('last_comment_timestamp')); +} + +/** + * @} End of "addtogroup updates-7.x-extra". + */ diff --git a/modules/forum/forum.module b/modules/forum/forum.module index eddac7978..7e8d81bde 100644 --- a/modules/forum/forum.module +++ b/modules/forum/forum.module @@ -233,7 +233,7 @@ function forum_entity_info_alter(&$info) { } /** - * Entity URI callback. + * Entity URI callback used in forum_entity_info_alter(). */ function forum_uri($forum) { return array( @@ -242,7 +242,7 @@ function forum_uri($forum) { } /** - * Check whether a content type can be used in a forum. + * Checks whether a node can be used in a forum, based on its content type. * * @param $node * A node object. @@ -283,7 +283,8 @@ function forum_node_view($node, $view_mode) { /** * Implements hook_node_validate(). * - * Check in particular that only a "leaf" term in the associated taxonomy. + * Checks in particular that the node is assigned only a "leaf" term in the + * forum taxonomy. */ function forum_node_validate($node, $form) { if (_forum_node_check_node_type($node)) { @@ -319,7 +320,7 @@ function forum_node_validate($node, $form) { /** * Implements hook_node_presave(). * - * Assign forum taxonomy when adding a topic from within a forum. + * Assigns the forum taxonomy when adding a topic from within a forum. */ function forum_node_presave($node) { if (_forum_node_check_node_type($node)) { @@ -481,7 +482,7 @@ function forum_taxonomy_term_delete($term) { /** * Implements hook_comment_publish(). * - * This actually handles the insert and update of published nodes since + * This actually handles the insertion and update of published nodes since * comment_save() calls hook_comment_publish() for all published comments. */ function forum_comment_publish($comment) { @@ -491,12 +492,12 @@ function forum_comment_publish($comment) { /** * Implements hook_comment_update(). * - * Comment module doesn't call hook_comment_unpublish() when saving individual - * comments so we need to check for those here. + * The Comment module doesn't call hook_comment_unpublish() when saving + * individual comments, so we need to check for those here. */ function forum_comment_update($comment) { - // comment_save() calls hook_comment_publish() for all published comments - // so we to handle all other values here. + // comment_save() calls hook_comment_publish() for all published comments, + // so we need to handle all other values here. if (!$comment->status) { _forum_update_forum_index($comment->nid); } @@ -669,8 +670,8 @@ function forum_block_save($delta = '', $edit = array()) { /** * Implements hook_block_view(). * - * Generates a block containing the currently active forum topics and the - * most recently added forum topics. + * Generates a block containing the currently active forum topics and the most + * recently added forum topics. */ function forum_block_view($delta = '') { $query = db_select('forum_index', 'f') @@ -700,13 +701,12 @@ function forum_block_view($delta = '') { } /** -* A #pre_render callback. Lists nodes based on the element's #query property. -* -* @see forum_block_view() -* -* @return -* A renderable array. -*/ + * Render API callback: Lists nodes based on the element's #query property. + * + * This function can be used as a #pre_render callback. + * + * @see forum_block_view() + */ function forum_block_view_pre_render($elements) { $result = $elements['#query']->execute(); if ($node_title_list = node_title_list($result)) { @@ -730,7 +730,7 @@ function forum_form($node, $form_state) { if (!empty($node->nid)) { $forum_terms = $node->taxonomy_forums; - // If editing, give option to leave shadows + // If editing, give option to leave shadows. $shadow = (count($forum_terms) > 1); $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.')); $form['forum_tid'] = array('#type' => 'value', '#value' => $node->forum_tid); @@ -743,13 +743,15 @@ function forum_form($node, $form_state) { * Returns a tree of all forums for a given taxonomy term ID. * * @param $tid - * (optional) Taxonomy ID of the forum, if not givin all forums will be returned. + * (optional) Taxonomy term ID of the forum. If not given all forums will be + * returned. + * * @return * A tree of taxonomy objects, with the following additional properties: - * - 'num_topics': Number of topics in the forum - * - 'num_posts': Total number of posts in all topics - * - 'last_post': Most recent post for the forum - * - 'forums': An array of child forums + * - num_topics: Number of topics in the forum. + * - num_posts: Total number of posts in all topics. + * - last_post: Most recent post for the forum. + * - forums: An array of child forums. */ function forum_forum_load($tid = NULL) { $cache = &drupal_static(__FUNCTION__, array()); @@ -857,8 +859,17 @@ function forum_forum_load($tid = NULL) { } /** - * Calculate the number of nodes the user has not yet read and are newer - * than NODE_NEW_LIMIT. + * Calculates the number of new posts in a forum that the user has not yet read. + * + * Nodes are new if they are newer than NODE_NEW_LIMIT. + * + * @param $term + * The term ID of the forum. + * @param $uid + * The user ID. + * + * @return + * The number of new posts in the forum that have not been read by the user. */ function _forum_topics_unread($term, $uid) { $query = db_select('node', 'n'); @@ -874,6 +885,23 @@ function _forum_topics_unread($term, $uid) { ->fetchField(); } +/** + * Gets all the topics in a forum. + * + * @param $tid + * The term ID of the forum. + * @param $sortby + * One of the following integers indicating the sort criteria: + * - 1: Date - newest first. + * - 2: Date - oldest first. + * - 3: Posts with the most comments first. + * - 4: Posts with the least comments first. + * @param $forum_per_page + * The maximum number of topics to display per page. + * + * @return + * A list of all the topics in a forum. + */ function forum_get_topics($tid, $sortby, $forum_per_page) { global $user, $forum_topic_list_header; @@ -944,7 +972,8 @@ function forum_get_topics($tid, $sortby, $forum_per_page) { $first_new_found = FALSE; foreach ($result as $topic) { if ($user->uid) { - // folder is new if topic is new or there are new comments since last visit + // A forum is new if the topic is new, or if there are new comments since + // the user's last visit. if ($topic->forum_tid != $tid) { $topic->new = 0; } @@ -981,15 +1010,22 @@ function forum_get_topics($tid, $sortby, $forum_per_page) { } /** - * Process variables for forums.tpl.php + * Preprocesses variables for forums.tpl.php. * - * The $variables array contains the following arguments: - * - $forums - * - $topics - * - $parents - * - $tid - * - $sortby - * - $forum_per_page + * @param $variables + * An array containing the following elements: + * - forums: An array of all forum objects to display for the given taxonomy + * term ID. If tid = 0 then all the top-level forums are displayed. + * - topics: An array of all the topics in the current forum. + * - parents: An array of taxonomy term objects that are ancestors of the + * current term ID. + * - tid: Taxonomy term ID of the current forum. + * - sortby: One of the following integers indicating the sort criteria: + * - 1: Date - newest first. + * - 2: Date - oldest first. + * - 3: Posts with the most comments first. + * - 4: Posts with the least comments first. + * - forum_per_page: The maximum number of topics to display per page. * * @see forums.tpl.php */ @@ -1060,12 +1096,15 @@ function template_preprocess_forums(&$variables) { } /** - * Process variables to format a forum listing. + * Preprocesses variables for forum-list.tpl.php. * - * $variables contains the following information: - * - $forums - * - $parents - * - $tid + * @param $variables + * An array containing the following elements: + * - forums: An array of all forum objects to display for the given taxonomy + * term ID. If tid = 0 then all the top-level forums are displayed. + * - parents: An array of taxonomy term objects that are ancestors of the + * current term ID. + * - tid: Taxonomy term ID of the current forum. * * @see forum-list.tpl.php * @see theme_forum_list() @@ -1106,13 +1145,13 @@ function template_preprocess_forum_list(&$variables) { } /** - * Preprocess variables to format the topic listing. + * Preprocesses variables for forum-topic-list.tpl.php. * - * $variables contains the following data: - * - $tid - * - $topics - * - $sortby - * - $forum_per_page + * @param $variables + * An array containing the following elements: + * - tid: Taxonomy term ID of the current forum. + * - topics: An array of all the topics in the current forum. + * - forum_per_page: The maximum number of topics to display per page. * * @see forum-topic-list.tpl.php * @see theme_forum_topic_list() @@ -1162,7 +1201,7 @@ function template_preprocess_forum_topic_list(&$variables) { } } else { - // Make this safe for the template + // Make this safe for the template. $variables['topics'] = array(); } // Give meaning to $tid for themers. $tid actually stands for term id. @@ -1173,14 +1212,16 @@ function template_preprocess_forum_topic_list(&$variables) { } /** - * Process variables to format the icon for each individual topic. + * Preprocesses variables for forum-icon.tpl.php. * - * $variables contains the following data: - * - $new_posts - * - $num_posts = 0 - * - $comment_mode = 0 - * - $sticky = 0 - * - $first_new + * @param $variables + * An array containing the following elements: + * - new_posts: Indicates whether or not the topic contains new posts. + * - num_posts: The total number of posts in all topics. + * - comment_mode: An integer indicating whether comments are open, closed, + * or hidden. + * - sticky: Indicates whether the topic is sticky. + * - first_new: Indicates whether this is the first topic with new posts. * * @see forum-icon.tpl.php * @see theme_forum_icon() @@ -1208,9 +1249,14 @@ function template_preprocess_forum_icon(&$variables) { } /** - * Process variables to format submission info for display in the forum list and topic list. + * Preprocesses variables for forum-submitted.tpl.php. + * + * The submission information will be displayed in the forum list and topic + * list. * - * $variables will contain: $topic + * @param $variables + * An array containing the following elements: + * - topic: The topic object. * * @see forum-submitted.tpl.php * @see theme_forum_submitted() @@ -1220,6 +1266,16 @@ function template_preprocess_forum_submitted(&$variables) { $variables['time'] = isset($variables['topic']->created) ? format_interval(REQUEST_TIME - $variables['topic']->created) : ''; } +/** + * Gets the last time the user viewed a node. + * + * @param $nid + * The node ID. + * + * @return + * The timestamp when the user last viewed this node, if the user has + * previously viewed the node; otherwise NODE_NEW_LIMIT. + */ function _forum_user_last_visit($nid) { global $user; $history = &drupal_static(__FUNCTION__, array()); @@ -1233,6 +1289,21 @@ function _forum_user_last_visit($nid) { return isset($history[$nid]) ? $history[$nid] : NODE_NEW_LIMIT; } +/** + * Gets topic sorting information based on an integer code. + * + * @param $sortby + * One of the following integers indicating the sort criteria: + * - 1: Date - newest first. + * - 2: Date - oldest first. + * - 3: Posts with the most comments first. + * - 4: Posts with the least comments first. + * + * @return + * An array with the following values: + * - field: A field for an SQL query. + * - sort: 'asc' or 'desc'. + */ function _forum_get_topic_order($sortby) { switch ($sortby) { case 1: diff --git a/modules/forum/forum.pages.inc b/modules/forum/forum.pages.inc index 29307e719..8538310ac 100644 --- a/modules/forum/forum.pages.inc +++ b/modules/forum/forum.pages.inc @@ -2,11 +2,20 @@ /** * @file - * User page callbacks for the forum module. + * User page callbacks for the Forum module. */ /** - * Menu callback; prints a forum listing. + * Page callback: Prints a forum listing. + * + * @param $forum_term + * A tree of all forums for a given taxonomy term ID. Defaults to NULL. See + * the return object of forum_forum_load() for a complete definition. + * + * @return + * A string containing HTML representing the themed forum listing. + * + * @see forum_menu() */ function forum_page($forum_term = NULL) { if (!isset($forum_term)) { diff --git a/modules/forum/forum.test b/modules/forum/forum.test index 612b09cff..d78d962de 100644 --- a/modules/forum/forum.test +++ b/modules/forum/forum.test @@ -5,14 +5,49 @@ * Tests for forum.module. */ +/** + * Provides automated tests for the Forum module. + */ class ForumTestCase extends DrupalWebTestCase { + + /** + * A user with various administrative privileges. + */ protected $admin_user; + + /** + * A user that can create forum topics and edit its own topics. + */ protected $edit_own_topics_user; + + /** + * A user that can create, edit, and delete forum topics. + */ protected $edit_any_topics_user; + + /** + * A user with no special privileges. + */ protected $web_user; + + /** + * An array representing a container. + */ protected $container; + + /** + * An array representing a forum. + */ protected $forum; + + /** + * An array representing a root forum. + */ protected $root_forum; + + /** + * An array of forum topic node IDs. + */ protected $nids; public static function getInfo() { @@ -23,9 +58,6 @@ class ForumTestCase extends DrupalWebTestCase { ); } - /** - * Enable modules and create users with specific permissions. - */ function setUp() { parent::setUp('taxonomy', 'comment', 'forum'); // Create users. @@ -53,12 +85,12 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Tests disabling and re-enabling forum. + * Tests disabling and re-enabling the Forum module. */ function testEnableForumField() { $this->drupalLogin($this->admin_user); - // Disable the forum module. + // Disable the Forum module. $edit = array(); $edit['modules[Core][forum][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); @@ -66,7 +98,7 @@ class ForumTestCase extends DrupalWebTestCase { module_list(TRUE); $this->assertFalse(module_exists('forum'), t('Forum module is not enabled.')); - // Attempt to re-enable the forum module and ensure it does not try to + // Attempt to re-enable the Forum module and ensure it does not try to // recreate the taxonomy_forums field. $edit = array(); $edit['modules[Core][forum][enable]'] = 'forum'; @@ -77,7 +109,7 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Login users, create forum nodes, and test forum functionality through the admin and user interfaces. + * Tests forum functionality through the admin and user interfaces. */ function testForum() { //Check that the basic forum install creates a default forum topic @@ -168,7 +200,10 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Forum nodes should not be created without choosing forum from select list. + * Tests that forum nodes can't be added without a parent. + * + * Verifies that forum nodes are not created without choosing "forum" from the + * select list. */ function testAddOrphanTopic() { // Must remove forum topics to test creating orphan topics. @@ -190,9 +225,10 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Run admin tests on the admin user. + * Runs admin tests on the admin user. * - * @param object $user The logged in user. + * @param object $user + * The logged in user. */ private function doAdminTests($user) { // Login the user. @@ -270,7 +306,7 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Edit the forum taxonomy. + * Edits the forum taxonomy. */ function editForumTaxonomy() { // Backup forum taxonomy. @@ -308,15 +344,16 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Create a forum container or a forum. + * Creates a forum container or a forum. * * @param $type - * Forum type (forum container or forum). + * The forum type (forum container or forum). * @param $parent - * Forum parent (default = 0 = a root forum; >0 = a forum container or + * The forum parent. This defaults to 0, indicating a root forum. * another forum). + * * @return - * taxonomy_term_data created. + * The created taxonomy term data. */ function createForum($type, $parent = 0) { // Generate a random name/description. @@ -349,7 +386,7 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Delete a forum. + * Deletes a forum. * * @param $tid * The forum ID. @@ -370,7 +407,7 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Run basic tests on the indicated user. + * Runs basic tests on the indicated user. * * @param $user * The logged in user. @@ -389,15 +426,15 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Create forum topic. + * Creates forum topic. * * @param array $forum - * Forum array. + * A forum array. * @param boolean $container - * True if $forum is a container. + * TRUE if $forum is a container; FALSE otherwise. * * @return object - * Topic node created. + * The created topic node. */ function createForumTopic($forum, $container = FALSE) { // Generate a random subject/body. @@ -439,7 +476,7 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Verify the logged in user has access to a forum nodes. + * Verifies that the logged in user has access to a forum nodes. * * @param $node_user * The user who creates the node. @@ -519,10 +556,12 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Verify display of forum page. + * Verifies display of forum page. * * @param $forum - * A row from taxonomy_term_data table in array. + * A row from the taxonomy_term_data table in an array. + * @param $parent + * (optional) An array representing the forum's parent. */ private function verifyForumView($forum, $parent = NULL) { // View forum page. @@ -542,9 +581,10 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * Generate forum topics to test display of active forum block. + * Generates forum topics to test the display of an active forum block. * - * @param array $forum Forum array (a row from taxonomy_term_data table). + * @param array $forum + * The foorum array (a row from taxonomy_term_data table). */ private function generateForumTopics($forum) { $this->nids = array(); @@ -555,10 +595,10 @@ class ForumTestCase extends DrupalWebTestCase { } /** - * View forum topics to test display of active forum block. + * Views forum topics to test the display of an active forum block. * - * @todo The logic here is completely incorrect, since the active - * forum topics block is determined by comments on the node, not by views. + * @todo The logic here is completely incorrect, since the active forum topics + * block is determined by comments on the node, not by views. * @todo DIE * * @param $nids diff --git a/modules/forum/forums.tpl.php b/modules/forum/forums.tpl.php index 55a760f57..6a0e02e66 100644 --- a/modules/forum/forums.tpl.php +++ b/modules/forum/forums.tpl.php @@ -2,16 +2,18 @@ /** * @file - * Default theme implementation to display a forum which may contain forum - * containers as well as forum topics. + * Displays a forum. * - * Variables available: - * - $forums: The forums to display (as processed by forum-list.tpl.php) - * - $topics: The topics to display (as processed by forum-topic-list.tpl.php) + * May contain forum containers as well as forum topics. + * + * Available variables: + * - $forums: The forums to display (as processed by forum-list.tpl.php). + * - $topics: The topics to display (as processed by forum-topic-list.tpl.php). * - $forums_defined: A flag to indicate that the forums are configured. * * @see template_preprocess_forums() - * @see theme_forums() + * + * @ingroup themeable */ ?> <?php if ($forums_defined): ?> diff --git a/modules/help/help.info b/modules/help/help.info index a973965cc..f92f0b9d7 100644 --- a/modules/help/help.info +++ b/modules/help/help.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = help.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/image/image.admin.inc b/modules/image/image.admin.inc index abcfab449..ab99a49e8 100644 --- a/modules/image/image.admin.inc +++ b/modules/image/image.admin.inc @@ -292,7 +292,7 @@ function image_style_name_validate($element, $form_state) { * @ingroup forms * @see image_style_delete_form_submit() */ -function image_style_delete_form($form, $form_state, $style) { +function image_style_delete_form($form, &$form_state, $style) { $form_state['image_style'] = $style; $replacement_styles = array_diff_key(image_style_options(), array($style['name'] => '')); diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc index 078f83fcc..5e80d88a7 100644 --- a/modules/image/image.field.inc +++ b/modules/image/image.field.inc @@ -204,11 +204,11 @@ function image_field_prepare_view($entity_type, $entities, $field, $instances, $ if (empty($items[$id])) { $fid = 0; // Use the default for the instance if one is available. - if ($instances[$id]['settings']['default_image']) { + if (!empty($instances[$id]['settings']['default_image'])) { $fid = $instances[$id]['settings']['default_image']; } // Otherwise, use the default for the field. - elseif ($field['settings']['default_image']) { + elseif (!empty($field['settings']['default_image'])) { $fid = $field['settings']['default_image']; } diff --git a/modules/image/image.info b/modules/image/image.info index ea3c2800c..b7b55c460 100644 --- a/modules/image/image.info +++ b/modules/image/image.info @@ -7,8 +7,8 @@ dependencies[] = file files[] = image.test configure = admin/config/media/image-styles -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/image/image.install b/modules/image/image.install index 5595a335d..b7aac7152 100644 --- a/modules/image/image.install +++ b/modules/image/image.install @@ -253,6 +253,11 @@ function image_update_7000() { } } +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Rename possibly misnamed {image_effect} table to {image_effects}. */ @@ -442,6 +447,10 @@ function image_update_7004() { } } +/** + * @} End of "addtogroup updates-7.x-extra". + */ + /** * Implements hook_requirements() to check the PHP GD Library. * diff --git a/modules/image/tests/image_module_test.info b/modules/image/tests/image_module_test.info index f94bb2575..d9a756bad 100644 --- a/modules/image/tests/image_module_test.info +++ b/modules/image/tests/image_module_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = image_module_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/locale/locale.datepicker.js b/modules/locale/locale.datepicker.js index 81f1e17b3..f81928238 100644 --- a/modules/locale/locale.datepicker.js +++ b/modules/locale/locale.datepicker.js @@ -1,69 +1,79 @@ (function ($) { -$.datepicker.regional['drupal-locale'] = { - closeText: Drupal.t('Done'), - prevText: Drupal.t('Prev'), - nextText: Drupal.t('Next'), - currentText: Drupal.t('Today'), - monthNames: [ - Drupal.t('January'), - Drupal.t('February'), - Drupal.t('March'), - Drupal.t('April'), - Drupal.t('May'), - Drupal.t('June'), - Drupal.t('July'), - Drupal.t('August'), - Drupal.t('September'), - Drupal.t('October'), - Drupal.t('November'), - Drupal.t('December') - ], - monthNamesShort: [ - Drupal.t('Jan'), - Drupal.t('Feb'), - Drupal.t('Mar'), - Drupal.t('Apr'), - Drupal.t('May'), - Drupal.t('Jun'), - Drupal.t('Jul'), - Drupal.t('Aug'), - Drupal.t('Sep'), - Drupal.t('Oct'), - Drupal.t('Nov'), - Drupal.t('Dec') - ], - dayNames: [ - Drupal.t('Sunday'), - Drupal.t('Monday'), - Drupal.t('Tuesday'), - Drupal.t('Wednesday'), - Drupal.t('Thursday'), - Drupal.t('Friday'), - Drupal.t('Saturday') - ], - dayNamesShort: [ - Drupal.t('Sun'), - Drupal.t('Mon'), - Drupal.t('Tue'), - Drupal.t('Wed'), - Drupal.t('Thu'), - Drupal.t('Fri'), - Drupal.t('Sat') - ], - dayNamesMin: [ - Drupal.t('Su'), - Drupal.t('Mo'), - Drupal.t('Tu'), - Drupal.t('We'), - Drupal.t('Th'), - Drupal.t('Fr'), - Drupal.t('Sa') - ], - dateFormat: Drupal.t('mm/dd/yy'), - firstDay: Drupal.settings.jqueryuidatepicker.firstDay, - isRTL: Drupal.settings.jqueryuidatepicker.rtl +/** + * Attaches language support to the jQuery UI datepicker component. + */ +Drupal.behaviors.localeDatepicker = { + attach: function(context, settings) { + // This code accesses Drupal.settings and localized strings via Drupal.t(). + // So this code should run after these are initialized. By placing it in an + // attach behavior this is assured. + $.datepicker.regional['drupal-locale'] = $.extend({ + closeText: Drupal.t('Done'), + prevText: Drupal.t('Prev'), + nextText: Drupal.t('Next'), + currentText: Drupal.t('Today'), + monthNames: [ + Drupal.t('January'), + Drupal.t('February'), + Drupal.t('March'), + Drupal.t('April'), + Drupal.t('May'), + Drupal.t('June'), + Drupal.t('July'), + Drupal.t('August'), + Drupal.t('September'), + Drupal.t('October'), + Drupal.t('November'), + Drupal.t('December') + ], + monthNamesShort: [ + Drupal.t('Jan'), + Drupal.t('Feb'), + Drupal.t('Mar'), + Drupal.t('Apr'), + Drupal.t('May'), + Drupal.t('Jun'), + Drupal.t('Jul'), + Drupal.t('Aug'), + Drupal.t('Sep'), + Drupal.t('Oct'), + Drupal.t('Nov'), + Drupal.t('Dec') + ], + dayNames: [ + Drupal.t('Sunday'), + Drupal.t('Monday'), + Drupal.t('Tuesday'), + Drupal.t('Wednesday'), + Drupal.t('Thursday'), + Drupal.t('Friday'), + Drupal.t('Saturday') + ], + dayNamesShort: [ + Drupal.t('Sun'), + Drupal.t('Mon'), + Drupal.t('Tue'), + Drupal.t('Wed'), + Drupal.t('Thu'), + Drupal.t('Fri'), + Drupal.t('Sat') + ], + dayNamesMin: [ + Drupal.t('Su'), + Drupal.t('Mo'), + Drupal.t('Tu'), + Drupal.t('We'), + Drupal.t('Th'), + Drupal.t('Fr'), + Drupal.t('Sa') + ], + dateFormat: Drupal.t('mm/dd/yy'), + firstDay: 0, + isRTL: 0 + }, Drupal.settings.jquery.ui.datepicker); + $.datepicker.setDefaults($.datepicker.regional['drupal-locale']); + } }; -$.datepicker.setDefaults($.datepicker.regional['drupal-locale']); })(jQuery); diff --git a/modules/locale/locale.info b/modules/locale/locale.info index 337223963..37d871d67 100644 --- a/modules/locale/locale.info +++ b/modules/locale/locale.info @@ -6,8 +6,8 @@ core = 7.x files[] = locale.test configure = admin/config/regional/language -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/locale/locale.install b/modules/locale/locale.install index cb7cefe01..2d94c7170 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -126,21 +126,21 @@ function locale_update_7002() { } /** - * Update "language_count" variable. + * @} End of "addtogroup updates-6.x-to-7.x". */ -function locale_update_7003() { - $languages = language_list('enabled'); - variable_set('language_count', count($languages[1])); -} /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @addtogroup updates-7.x-extra + * @{ */ /** - * @addtogroup updates-7.x-extra - * @{ + * Update "language_count" variable. */ +function locale_update_7003() { + $languages = language_list('enabled'); + variable_set('language_count', count($languages[1])); +} /** * Remove duplicates in {locales_source}. @@ -202,7 +202,7 @@ function locale_update_7004() { } /** - * @} End of "addtogroup updates-7.x-extra" + * @} End of "addtogroup updates-7.x-extra". */ /** diff --git a/modules/locale/locale.module b/modules/locale/locale.module index e0981b2fb..a20f3d5d7 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -396,7 +396,7 @@ function locale_form_node_form_alter(&$form, &$form_state) { function locale_field_node_form_submit($form, &$form_state) { if (field_has_translation_handler('node', 'locale')) { $node = (object) $form_state['values']; - $available_languages = field_content_languages(); + $current_language = entity_language('node', $node); list(, , $bundle) = entity_extract_ids('node', $node); foreach (field_info_instances('node', $bundle) as $instance) { @@ -406,8 +406,8 @@ function locale_field_node_form_submit($form, &$form_state) { // Handle a possible language change: new language values are inserted, // previous ones are deleted. - if ($field['translatable'] && $previous_language != $node->language) { - $form_state['values'][$field_name][$node->language] = $node->{$field_name}[$previous_language]; + if ($field['translatable'] && $previous_language != $current_language) { + $form_state['values'][$field_name][$current_language] = $node->{$field_name}[$previous_language]; $form_state['values'][$field_name][$previous_language] = array(); } } @@ -758,9 +758,8 @@ function locale_get_plural($count, $langcode = NULL) { if (!isset($plural_indexes[$langcode][$count])) { // Retrieve and statically cache the plural formulas for all languages. if (empty($plural_formulas)) { - $language_list = language_list(); - foreach ($language_list as $langcode => $lang) { - $plural_formulas[$langcode] = $lang->formula; + foreach (language_list() as $installed_language) { + $plural_formulas[$installed_language->language] = $installed_language->formula; } } // If there is a plural formula for the language, evaluate it for the given @@ -951,15 +950,22 @@ function locale_css_alter(&$css) { * Provides the language support for the jQuery UI Date Picker. */ function locale_library_alter(&$libraries, $module) { - global $language; - if ($module == 'system' && isset($libraries['system']['ui.datepicker'])) { + if ($module == 'system' && isset($libraries['ui.datepicker'])) { + global $language; + // locale.datepicker.js should be added in the JS_LIBRARY group, so that + // this attach behavior will execute early. JS_LIBRARY is the default for + // hook_library_info_alter(), thus does not have to be specified explicitly. $datepicker = drupal_get_path('module', 'locale') . '/locale.datepicker.js'; - $libraries['system']['ui.datepicker']['js'][$datepicker] = array('group' => JS_THEME); - $libraries['system']['ui.datepicker']['js'][] = array( + $libraries['ui.datepicker']['js'][$datepicker] = array(); + $libraries['ui.datepicker']['js'][] = array( 'data' => array( - 'jqueryuidatepicker' => array( - 'rtl' => $language->direction == LANGUAGE_RTL, - 'firstDay' => variable_get('date_first_day', 0), + 'jquery' => array( + 'ui' => array( + 'datepicker' => array( + 'isRTL' => $language->direction == LANGUAGE_RTL, + 'firstDay' => variable_get('date_first_day', 0), + ), + ), ), ), 'type' => 'setting', diff --git a/modules/locale/locale.test b/modules/locale/locale.test index ffda6f580..6f3135b08 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -179,6 +179,36 @@ class LocaleConfigurationTest extends DrupalWebTestCase { } +/** + * Tests localization of the JavaScript libraries. + * + * Currently, only the jQuery datepicker is localized using Drupal translations. + */ +class LocaleLibraryInfoAlterTest extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Javascript library localisation', + 'description' => 'Tests the localisation of JavaScript libraries.', + 'group' => 'Locale', + ); + } + + function setUp() { + parent::setUp('locale', 'locale_test'); + } + + /** + * Verifies that the datepicker can be localized. + * + * @see locale_library_info_alter() + */ + public function testLibraryInfoAlter() { + drupal_add_library('system', 'ui.datepicker'); + $scripts = drupal_get_js(); + $this->assertTrue(strpos($scripts, 'locale.datepicker.js'), t('locale.datepicker.js added to scripts.')); + } +} + /** * Functional tests for JavaScript parsing for translatable strings. */ @@ -605,7 +635,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $this->assertText(t('No strings available.'), t("Search didn't find the string.")); // Ensure untranslated string appears if searching on 'only untranslated - // strings'. + // strings' in "all" (hasn't been translated to any language). $search = array( 'string' => $name, 'language' => 'all', @@ -615,6 +645,17 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); $this->assertNoText(t('No strings available.'), t('Search found the string.')); + // Ensure untranslated string appears if searching on 'only untranslated + // strings' in the custom language (hasn't been translated to that specific language). + $search = array( + 'string' => $name, + 'language' => $langcode, + 'translation' => 'untranslated', + 'group' => 'all', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + $this->assertNoText(t('No strings available.'), t('Search found the string.')); + // Add translation. // Assume this is the only result, given the random name. $this->clickLink(t('edit')); @@ -1515,7 +1556,6 @@ class LocaleUninstallFrenchFunctionalTest extends LocaleUninstallFunctionalTest } } - /** * Functional tests for the language switching feature. */ @@ -1690,7 +1730,6 @@ class LocaleBrowserDetectionTest extends DrupalUnitTestCase { 'eh' => 'eh-oh-laa-laa', // Different qvalues. - 'en-US,en;q=0.5,fr;q=0.25' => 'en-US', 'fr,en;q=0.5' => 'fr-CA', 'fr,en;q=0.5,fr-CA;q=0.25' => 'fr', @@ -2378,7 +2417,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase { array( 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), 'locale_language_negotiation_url_part' => LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN, - 'locale_test_domain' => $language_domain, + 'locale_test_domain' => $language_domain . ':88', 'path' => 'admin/config', 'expect' => $language_string, 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL, @@ -2788,12 +2827,14 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase { ->orderBy('cid', 'DESC') ->execute() ->fetchObject(); - $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->language, '%langcode' => $langcode); - $this->assertEqual($comment->language, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args)); + $comment_langcode = entity_language('comment', $comment); + $args = array('%node_language' => $node_langcode, '%comment_language' => $comment_langcode, '%langcode' => $langcode); + $this->assertEqual($comment_langcode, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args)); } } } } + /** * Functional tests for localizing date formats. */ diff --git a/modules/locale/tests/locale_test.info b/modules/locale/tests/locale_test.info index 2923f9ab9..ce010a6ad 100644 --- a/modules/locale/tests/locale_test.info +++ b/modules/locale/tests/locale_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/menu/menu.info b/modules/menu/menu.info index 766998a42..0f4e8a4f3 100644 --- a/modules/menu/menu.info +++ b/modules/menu/menu.info @@ -6,8 +6,8 @@ core = 7.x files[] = menu.test configure = admin/structure/menu -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/menu/menu.install b/modules/menu/menu.install index 7877b996d..346edf9f2 100644 --- a/modules/menu/menu.install +++ b/modules/menu/menu.install @@ -70,7 +70,7 @@ function menu_uninstall() { } /** - * @defgroup updates-7.x-extra Extra updates for 7.x + * @addtogroup updates-7.x-extra * @{ */ @@ -205,6 +205,6 @@ function menu_update_7003(&$sandbox) { } /** - * @} End of "defgroup updates-7.x-extra" + * @} End of "addtogroup updates-7.x-extra". * The next series of updates should start at 8000. */ diff --git a/modules/node/content_types.inc b/modules/node/content_types.inc index 71b3b1a60..72adc3491 100644 --- a/modules/node/content_types.inc +++ b/modules/node/content_types.inc @@ -437,7 +437,7 @@ function node_type_delete_confirm_submit($form, &$form_state) { variable_del('node_preview_' . $form_state['values']['type']); $t_args = array('%name' => $form_state['values']['name']); drupal_set_message(t('The content type %name has been deleted.', $t_args)); - watchdog('menu', 'Deleted content type %name.', $t_args, WATCHDOG_NOTICE); + watchdog('node', 'Deleted content type %name.', $t_args, WATCHDOG_NOTICE); node_types_rebuild(); menu_rebuild(); diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index a1967c45d..1508bc054 100644 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -432,7 +432,8 @@ function node_admin_nodes() { $destination = drupal_get_destination(); $options = array(); foreach ($nodes as $node) { - $l_options = $node->language != LANGUAGE_NONE && isset($languages[$node->language]) ? array('language' => $languages[$node->language]) : array(); + $langcode = entity_language('node', $node); + $l_options = $langcode != LANGUAGE_NONE && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array(); $options[$node->nid] = array( 'title' => array( 'data' => array( @@ -449,11 +450,11 @@ function node_admin_nodes() { 'changed' => format_date($node->changed, 'short'), ); if ($multilanguage) { - if ($node->language == LANGUAGE_NONE || isset($languages[$node->language])) { - $options[$node->nid]['language'] = $node->language == LANGUAGE_NONE ? t('Language neutral') : t($languages[$node->language]->name); + if ($langcode == LANGUAGE_NONE || isset($languages[$langcode])) { + $options[$node->nid]['language'] = $langcode == LANGUAGE_NONE ? t('Language neutral') : t($languages[$langcode]->name); } else { - $options[$node->nid]['language'] = t('Undefined language (@langcode)', array('@langcode' => $node->language)); + $options[$node->nid]['language'] = t('Undefined language (@langcode)', array('@langcode' => $langcode)); } } // Build a list of all the accessible operations for the current node. diff --git a/modules/node/node.info b/modules/node/node.info index c15b4944a..d53ee84c9 100644 --- a/modules/node/node.info +++ b/modules/node/node.info @@ -9,8 +9,8 @@ required = TRUE configure = admin/structure/types stylesheets[all][] = node.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/node/node.install b/modules/node/node.install index c54ebfbe3..434410c8d 100644 --- a/modules/node/node.install +++ b/modules/node/node.install @@ -846,6 +846,15 @@ function node_update_7010() { db_create_table('block_node_type', $schema['block_node_type']); } +/** + * @} End of "addtogroup updates-6.x-to-7.x". + */ + +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Update the database from Drupal 6 to match the schema. */ @@ -890,7 +899,7 @@ function node_update_7012() { ->execute(); // Switch field languages to LANGUAGE_NONE, since initially they were - // assigned $node->language. + // assigned the node language. foreach (array('field_data_body', 'field_revision_body') as $table) { db_update($table) ->fields(array('language' => LANGUAGE_NONE)) @@ -915,5 +924,5 @@ function node_update_7013() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-7.x-extra". */ diff --git a/modules/node/node.module b/modules/node/node.module index 57133c6b0..264816c00 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -177,6 +177,7 @@ function node_entity_info() { 'revision' => 'vid', 'bundle' => 'type', 'label' => 'title', + 'language' => 'language', ), 'bundle keys' => array( 'bundle' => 'type', @@ -655,7 +656,7 @@ function node_type_update_nodes($old_type, $type) { * @param $rebuild * TRUE to rebuild node types. Equivalent to calling node_types_rebuild(). * @return - * Associative array with two components: + * An object with two properties: * - names: Associative array of the names of node types, keyed by the type. * - types: Associative array of node type objects, keyed by the type. * Both of these arrays will include new types that have been defined by @@ -1002,8 +1003,6 @@ function node_validate($node, $form, &$form_state) { * Prepare node for saving by populating author and creation date. */ function node_submit($node) { - global $user; - // A user might assign the node author by entering a user name in the node // form, which we then need to translate to a user ID. if (isset($node->name)) { @@ -1684,7 +1683,7 @@ function node_search_execute($keys = NULL, $conditions = NULL) { 'extra' => $extra, 'score' => $item->calculated_score, 'snippet' => search_excerpt($keys, $node->rendered), - 'language' => $node->language, + 'language' => entity_language('node', $node), ); } return $results; @@ -3274,8 +3273,9 @@ function _node_query_node_access_alter($query, $type) { // @endcode // // So instead of directly adding to the query object, we need to collect - // in a separate db_and() object and then at the end add it to the query. - $entity_conditions = db_and(); + // all of the node access conditions in a separate db_and() object and + // then add it to the query at the end. + $node_conditions = db_and(); } foreach ($tables as $nalias => $tableinfo) { $table = $tableinfo['table']; @@ -3309,16 +3309,24 @@ function _node_query_node_access_alter($query, $type) { $field = 'entity_id'; } $subquery->where("$nalias.$field = na.nid"); - $query->exists($subquery); + + // For an entity query, attach the subquery to entity conditions. + if ($type == 'entity') { + $node_conditions->exists($subquery); + } + // Otherwise attach it to the node query itself. + else { + $query->exists($subquery); + } } } if ($type == 'entity' && count($subquery->conditions())) { // All the node access conditions are only for field values belonging to // nodes. - $entity_conditions->condition("$base_alias.entity_type", 'node'); + $node_conditions->condition("$base_alias.entity_type", 'node'); $or = db_or(); - $or->condition($entity_conditions); + $or->condition($node_conditions); // If the field value belongs to a non-node entity type then this function // does not do anything with it. $or->condition("$base_alias.entity_type", 'node', '<>'); diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index 019ed3e8c..1f3e725de 100644 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -15,6 +15,13 @@ function node_page_edit($node) { return drupal_get_form($node->type . '_node_form', $node); } +/** + * Page callback: Displays add content links for available content types. + * + * Redirects to node/add/[type] if only one content type is available. + * + * @see node_menu() + */ function node_add_page() { $item = menu_get_item(); $content = system_admin_menu_block($item); @@ -299,7 +306,7 @@ function node_form($form, &$form_state, $node) { } $form += array('#submit' => array()); - field_attach_form('node', $node, $form, $form_state, $node->language); + field_attach_form('node', $node, $form, $form_state, entity_language('node', $node)); return $form; } diff --git a/modules/node/node.test b/modules/node/node.test index f46d2a108..37d05e529 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -5,6 +5,26 @@ * Tests for node.module. */ +/** + * Defines a base class for testing the Node module. + */ +class NodeWebTestCase extends DrupalWebTestCase { + function setUp() { + $modules = func_get_args(); + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; + } + $modules[] = 'node'; + parent::setUp($modules); + + // Create Basic page and Article node types. + if ($this->profile != 'standard') { + $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); + $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); + } + } +} + /** * Test the node_load_multiple() function. */ @@ -2268,15 +2288,16 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase { // Generate and test sanitized tokens. $tests = array(); + $langcode = entity_language('node', $node); $tests['[node:nid]'] = $node->nid; $tests['[node:vid]'] = $node->vid; $tests['[node:tnid]'] = $node->tnid; $tests['[node:type]'] = 'article'; $tests['[node:type-name]'] = 'Article'; $tests['[node:title]'] = check_plain($node->title); - $tests['[node:body]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'value'); - $tests['[node:summary]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'summary'); - $tests['[node:language]'] = check_plain($node->language); + $tests['[node:body]'] = _text_sanitize($instance, $langcode, $node->body[$langcode][0], 'value'); + $tests['[node:summary]'] = _text_sanitize($instance, $langcode, $node->body[$langcode][0], 'summary'); + $tests['[node:language]'] = check_plain($langcode); $tests['[node:url]'] = url('node/' . $node->nid, $url_options); $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options); $tests['[node:author]'] = check_plain(format_username($account)); @@ -2295,9 +2316,9 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase { // Generate and test unsanitized tokens. $tests['[node:title]'] = $node->title; - $tests['[node:body]'] = $node->body[$node->language][0]['value']; - $tests['[node:summary]'] = $node->body[$node->language][0]['summary']; - $tests['[node:language]'] = $node->language; + $tests['[node:body]'] = $node->body[$langcode][0]['value']; + $tests['[node:summary]'] = $node->body[$langcode][0]['summary']; + $tests['[node:language]'] = $langcode; $tests['[node:author:name]'] = format_username($account); foreach ($tests as $input => $expected) { @@ -2493,3 +2514,78 @@ class NodeAccessPagerTestCase extends DrupalWebTestCase { $this->assertNoRaw('page=2', t('No third page exists.')); } } + + +/** + * Tests the interaction of the node access system with fields. + */ +class NodeAccessFieldTestCase extends NodeWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Node access and fields', + 'description' => 'Tests the interaction of the node access system with fields.', + 'group' => 'Node', + ); + } + + public function setUp() { + parent::setUp('node_access_test', 'field_ui'); + node_access_rebuild(); + + // Create some users. + $this->admin_user = $this->drupalCreateUser(array('access content', 'bypass node access')); + $this->content_admin_user = $this->drupalCreateUser(array('access content', 'administer content types')); + + // Add a custom field to the page content type. + $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); + $this->field = field_create_field(array('field_name' => $this->field_name, 'type' => 'text')); + $this->instance = field_create_instance(array( + 'field_name' => $this->field_name, + 'entity_type' => 'node', + 'bundle' => 'page', + )); + } + + /** + * Tests administering fields when node access is restricted. + */ + function testNodeAccessAdministerField() { + // Create a page node. + $langcode = LANGUAGE_NONE; + $field_data = array(); + $value = $field_data[$langcode][0]['value'] = $this->randomName(); + $node = $this->drupalCreateNode(array($this->field_name => $field_data)); + + // Log in as the administrator and confirm that the field value is present. + $this->drupalLogin($this->admin_user); + $this->drupalGet("node/{$node->nid}"); + $this->assertText($value, 'The saved field value is visible to an administrator.'); + + // Log in as the content admin and try to view the node. + $this->drupalLogin($this->content_admin_user); + $this->drupalGet("node/{$node->nid}"); + $this->assertText('Access denied', 'Access is denied for the content admin.'); + + // Modify the field default as the content admin. + $edit = array(); + $default = 'Sometimes words have two meanings'; + $edit["{$this->field_name}[$langcode][0][value]"] = $default; + $this->drupalPost( + "admin/structure/types/manage/page/fields/{$this->field_name}", + $edit, + t('Save settings') + ); + + // Log in as the administrator. + $this->drupalLogin($this->admin_user); + + // Confirm that the existing node still has the correct field value. + $this->drupalGet("node/{$node->nid}"); + $this->assertText($value, 'The original field value is visible to an administrator.'); + + // Confirm that the new default value appears when creating a new node. + $this->drupalGet('node/add/page'); + $this->assertRaw($default, 'The updated default value is displayed when creating a new node.'); + } +} diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc index 491ec81c4..e43db5e7d 100644 --- a/modules/node/node.tokens.inc +++ b/modules/node/node.tokens.inc @@ -144,7 +144,8 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr break; case 'language': - $replacements[$original] = $sanitize ? check_plain($node->language) : $node->language; + $langcode = entity_language('node', $node); + $replacements[$original] = $sanitize ? check_plain($langcode) : $langcode; break; case 'url': diff --git a/modules/node/tests/node_access_test.info b/modules/node/tests/node_access_test.info index df12ab45a..ade2ccc95 100644 --- a/modules/node/tests/node_access_test.info +++ b/modules/node/tests/node_access_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/node/tests/node_test.info b/modules/node/tests/node_test.info index 3fe505a09..69d0f3bd9 100644 --- a/modules/node/tests/node_test.info +++ b/modules/node/tests/node_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/node/tests/node_test_exception.info b/modules/node/tests/node_test_exception.info index d2daca052..9b4459177 100644 --- a/modules/node/tests/node_test_exception.info +++ b/modules/node/tests/node_test_exception.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/openid/openid.info b/modules/openid/openid.info index 078c58345..aaddb3461 100644 --- a/modules/openid/openid.info +++ b/modules/openid/openid.info @@ -5,8 +5,8 @@ package = Core core = 7.x files[] = openid.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/openid/openid.install b/modules/openid/openid.install index 2df39aa69..4b77b710b 100644 --- a/modules/openid/openid.install +++ b/modules/openid/openid.install @@ -156,5 +156,5 @@ function openid_update_6000() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/openid/tests/openid_test.info b/modules/openid/tests/openid_test.info index 83e1beff8..d3ece3ddf 100644 --- a/modules/openid/tests/openid_test.info +++ b/modules/openid/tests/openid_test.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = openid hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/overlay/overlay.info b/modules/overlay/overlay.info index 1b9825235..1331fc605 100644 --- a/modules/overlay/overlay.info +++ b/modules/overlay/overlay.info @@ -4,8 +4,8 @@ package = Core version = VERSION core = 7.x -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/path/path.info b/modules/path/path.info index 7b6c07291..8e2cb8b90 100644 --- a/modules/path/path.info +++ b/modules/path/path.info @@ -6,8 +6,8 @@ core = 7.x files[] = path.test configure = admin/config/search/path -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/path/path.module b/modules/path/path.module index 9df498865..1bb06f42b 100644 --- a/modules/path/path.module +++ b/modules/path/path.module @@ -99,8 +99,9 @@ function path_form_node_form_alter(&$form, $form_state) { $path = array(); if (!empty($form['#node']->nid)) { $conditions = array('source' => 'node/' . $form['#node']->nid); - if ($form['#node']->language != LANGUAGE_NONE) { - $conditions['language'] = $form['#node']->language; + $langcode = entity_language('node', $form['#node']); + if ($langcode != LANGUAGE_NONE) { + $conditions['language'] = $langcode; } $path = path_load($conditions); if ($path === FALSE) { @@ -111,7 +112,7 @@ function path_form_node_form_alter(&$form, $form_state) { 'pid' => NULL, 'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL, 'alias' => '', - 'language' => isset($form['#node']->language) ? $form['#node']->language : LANGUAGE_NONE, + 'language' => isset($langcode) ? $langcode : LANGUAGE_NONE, ); $form['path'] = array( @@ -192,8 +193,9 @@ function path_node_insert($node) { // Only save a non-empty alias. if (!empty($path['alias'])) { // Ensure fields for programmatic executions. + $langcode = entity_language('node', $node); $path['source'] = 'node/' . $node->nid; - $path['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE; + $path['language'] = isset($langcode) ? $langcode : LANGUAGE_NONE; path_save($path); } } @@ -210,13 +212,7 @@ function path_node_update($node) { if (!empty($path['pid']) && empty($path['alias'])) { path_delete($path['pid']); } - // Only save a non-empty alias. - if (!empty($path['alias'])) { - // Ensure fields for programmatic executions. - $path['source'] = 'node/' . $node->nid; - $path['language'] = isset($node->language) ? $node->language : LANGUAGE_NONE; - path_save($path); - } + path_node_insert($node); } } @@ -234,7 +230,10 @@ function path_node_delete($node) { function path_form_taxonomy_form_term_alter(&$form, $form_state) { // Make sure this does not show up on the delete confirmation form. if (empty($form_state['confirm_delete'])) { - $path = (isset($form['#term']['tid']) ? path_load('taxonomy/term/' . $form['#term']['tid']) : array()); + $langcode = entity_language('taxonomy_term', (object) $form['#term']); + $langcode = !empty($langcode) ? $langcode : LANGUAGE_NONE; + $conditions = array('source' => 'taxonomy/term/' . $form['#term']['tid'], 'language' => $langcode); + $path = (isset($form['#term']['tid']) ? path_load($conditions) : array()); if ($path === FALSE) { $path = array(); } @@ -242,7 +241,7 @@ function path_form_taxonomy_form_term_alter(&$form, $form_state) { 'pid' => NULL, 'source' => isset($form['#term']['tid']) ? 'taxonomy/term/' . $form['#term']['tid'] : NULL, 'alias' => '', - 'language' => LANGUAGE_NONE, + 'language' => $langcode, ); $form['path'] = array( '#access' => user_access('create url aliases') || user_access('administer url aliases'), @@ -274,7 +273,10 @@ function path_taxonomy_term_insert($term) { if (!empty($path['alias'])) { // Ensure fields for programmatic executions. $path['source'] = 'taxonomy/term/' . $term->tid; - $path['language'] = LANGUAGE_NONE; + // Core does not provide a way to store the term language but contrib + // modules can do it so we need to take this into account. + $langcode = entity_language('taxonomy_term', $term); + $path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE; path_save($path); } } @@ -295,7 +297,10 @@ function path_taxonomy_term_update($term) { if (!empty($path['alias'])) { // Ensure fields for programmatic executions. $path['source'] = 'taxonomy/term/' . $term->tid; - $path['language'] = LANGUAGE_NONE; + // Core does not provide a way to store the term language but contrib + // modules can do it so we need to take this into account. + $langcode = entity_language('taxonomy_term', $term); + $path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE; path_save($path); } } diff --git a/modules/php/php.info b/modules/php/php.info index 18a7f2f5c..241c8ee23 100644 --- a/modules/php/php.info +++ b/modules/php/php.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = php.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/php/php.test b/modules/php/php.test index 50fb55283..b68bd501d 100644 --- a/modules/php/php.test +++ b/modules/php/php.test @@ -33,8 +33,8 @@ class PHPTestCase extends DrupalWebTestCase { // Verify that anonymous and authenticated user roles do not have access. $this->drupalGet('admin/config/content/formats/' . $php_format_id); - $this->assertFieldByName('roles[1]', FALSE, t('Anonymous users do not have access to PHP code format.')); - $this->assertFieldByName('roles[2]', FALSE, t('Authenticated users do not have access to PHP code format.')); + $this->assertFieldByName('roles[' . DRUPAL_ANONYMOUS_RID . ']', FALSE, t('Anonymous users do not have access to PHP code format.')); + $this->assertFieldByName('roles[' . DRUPAL_AUTHENTICATED_RID . ']', FALSE, t('Authenticated users do not have access to PHP code format.')); } /** diff --git a/modules/poll/poll.info b/modules/poll/poll.info index 93cbb0f89..3b4aea98a 100644 --- a/modules/poll/poll.info +++ b/modules/poll/poll.info @@ -6,8 +6,8 @@ core = 7.x files[] = poll.test stylesheets[all][] = poll.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/poll/poll.install b/modules/poll/poll.install index 8c73cf43a..8c58025bb 100644 --- a/modules/poll/poll.install +++ b/modules/poll/poll.install @@ -197,6 +197,11 @@ function poll_update_7003() { )); } +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Update the database to match the schema. */ @@ -204,3 +209,7 @@ function poll_update_7004() { // Remove field default. db_change_field('poll_vote', 'chid', 'chid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE)); } + +/** + * @} End of "addtogroup updates-7.x-extra". + */ diff --git a/modules/profile/profile.info b/modules/profile/profile.info index 88de22523..e90986c33 100644 --- a/modules/profile/profile.info +++ b/modules/profile/profile.info @@ -11,8 +11,8 @@ configure = admin/config/people/profile ; See user_system_info_alter(). hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/rdf/rdf.info b/modules/rdf/rdf.info index c02ddeee2..f04ab1d10 100644 --- a/modules/rdf/rdf.info +++ b/modules/rdf/rdf.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = rdf.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/rdf/tests/rdf_test.info b/modules/rdf/tests/rdf_test.info index 34ec20b4b..6b18b3532 100644 --- a/modules/rdf/tests/rdf_test.info +++ b/modules/rdf/tests/rdf_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/search/search.info b/modules/search/search.info index fbea0faab..a859f9295 100644 --- a/modules/search/search.info +++ b/modules/search/search.info @@ -8,8 +8,8 @@ files[] = search.test configure = admin/config/search/settings stylesheets[all][] = search.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/search/tests/search_embedded_form.info b/modules/search/tests/search_embedded_form.info index 17d5f6117..6ec3759bf 100644 --- a/modules/search/tests/search_embedded_form.info +++ b/modules/search/tests/search_embedded_form.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/search/tests/search_extra_type.info b/modules/search/tests/search_extra_type.info index 241a86db6..d9e3c8993 100644 --- a/modules/search/tests/search_extra_type.info +++ b/modules/search/tests/search_extra_type.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/shortcut/shortcut.info b/modules/shortcut/shortcut.info index 7a135d005..36820cd53 100644 --- a/modules/shortcut/shortcut.info +++ b/modules/shortcut/shortcut.info @@ -6,8 +6,8 @@ core = 7.x files[] = shortcut.test configure = admin/config/user-interface/shortcut -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 251c5c1bc..d83dbeb6f 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -85,6 +85,10 @@ abstract class DrupalTestCase { */ protected $setup = FALSE; + protected $setupDatabasePrefix = FALSE; + + protected $setupEnvironment = FALSE; + /** * Constructor for DrupalTestCase. * @@ -562,14 +566,21 @@ abstract class DrupalTestCase { /** * Generates a random string of ASCII characters of codes 32 to 126. * - * The generated string includes alpha-numeric characters and common misc - * characters. Use this method when testing general input where the content - * is not restricted. + * The generated string includes alpha-numeric characters and common + * miscellaneous characters. Use this method when testing general input + * where the content is not restricted. + * + * Do not use this method when special characters are not possible (e.g., in + * machine or file names that have already been validated); instead, + * use DrupalWebTestCase::randomName(). * * @param $length * Length of random string to generate. + * * @return * Randomly generated string. + * + * @see DrupalWebTestCase::randomName() */ public static function randomString($length = 8) { $str = ''; @@ -588,10 +599,16 @@ abstract class DrupalTestCase { * require machine readable values (i.e. without spaces and non-standard * characters) this method is best. * + * Do not use this method when testing unvalidated user input. Instead, use + * DrupalWebTestCase::randomString(). + * * @param $length * Length of random string to generate. + * * @return * Randomly generated string. + * + * @see DrupalWebTestCase::randomString() */ public static function randomName($length = 8) { $values = array_merge(range(65, 90), range(97, 122), range(48, 57)); @@ -1251,29 +1268,46 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Generates a random database prefix, runs the install scripts on the - * prefixed database and enable the specified modules. After installation - * many caches are flushed and the internal browser is setup so that the - * page requests will run on the new prefix. A temporary files directory - * is created with the same name as the database prefix. + * Generates a database prefix for running tests. * - * @param ... - * List of modules to enable for the duration of the test. This can be - * either a single array or a variable number of string arguments. + * The generated database table prefix is used for the Drupal installation + * being performed for the test. It is also used as user agent HTTP header + * value by the cURL-based browser of DrupalWebTestCase, which is sent + * to the Drupal installation of the test. During early Drupal bootstrap, the + * user agent HTTP header is parsed, and if it matches, all database queries + * use the database table prefix that has been generated here. + * + * @see DrupalWebTestCase::curlInitialize() + * @see drupal_valid_test_ua() + * @see DrupalWebTestCase::setUp() */ - protected function setUp() { - global $user, $language, $conf; - - // Generate a temporary prefixed database to ensure that tests have a clean starting point. + protected function prepareDatabasePrefix() { $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); + + // As soon as the database prefix is set, the test might start to execute. + // All assertions as well as the SimpleTest batch operations are associated + // with the testId, so the database prefix has to be associated with it. db_update('simpletest_test_id') ->fields(array('last_prefix' => $this->databasePrefix)) ->condition('test_id', $this->testId) ->execute(); + } - // Reset all statics and variables to perform tests in a clean environment. - $conf = array(); - drupal_static_reset(); + /** + * Changes the database connection to the prefixed one. + * + * @see DrupalWebTestCase::setUp() + */ + protected function changeDatabasePrefix() { + if (empty($this->databasePrefix)) { + $this->prepareDatabasePrefix(); + // If $this->prepareDatabasePrefix() failed to work, return without + // setting $this->setupDatabasePrefix to TRUE, so setUp() methods will + // know to bail out. + if (empty($this->databasePrefix)) { + return; + } + } // Clone the current connection and replace the current prefix. $connection_info = Database::getConnectionInfo('default'); @@ -1285,22 +1319,43 @@ class DrupalWebTestCase extends DrupalTestCase { } Database::addConnectionInfo('default', 'default', $connection_info['default']); + // Indicate the database prefix was set up correctly. + $this->setupDatabasePrefix = TRUE; + } + + /** + * Prepares the current environment for running the test. + * + * Backups various current environment variables and resets them, so they do + * not interfere with the Drupal site installation in which tests are executed + * and can be restored in tearDown(). + * + * Also sets up new resources for the testing environment, such as the public + * filesystem and configuration directories. + * + * @see DrupalWebTestCase::setUp() + * @see DrupalWebTestCase::tearDown() + */ + protected function prepareEnvironment() { + global $user, $language, $conf; + // Store necessary current values before switching to prefixed database. $this->originalLanguage = $language; $this->originalLanguageDefault = variable_get('language_default'); $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); $this->originalProfile = drupal_get_profile(); - $clean_url_original = variable_get('clean_url', 0); + $this->originalCleanUrl = variable_get('clean_url', 0); + $this->originalUser = $user; // Set to English to prevent exceptions from utf8_truncate() from t() // during install if the current language is not 'en'. // The following array/object conversion is copied from language_default(). $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''); - // Save and clean shutdown callbacks array because it static cached and - // will be changed by the test run. If we don't, then it will contain - // callbacks from both environments. So testing environment will try - // to call handlers from original environment. + // Save and clean the shutdown callbacks array because it is static cached + // and will be changed by the test run. Otherwise it will contain callbacks + // from both environments and the testing environment will try to call the + // handlers defined by the original one. $callbacks = &drupal_register_shutdown_function(); $this->originalShutdownCallbacks = $callbacks; $callbacks = array(); @@ -1308,25 +1363,75 @@ class DrupalWebTestCase extends DrupalTestCase { // Create test directory ahead of installation so fatal errors and debug // information can be logged during installation process. // Use temporary files directory with the same prefix as the database. - $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - $private_files_directory = $public_files_directory . '/private'; - $temp_files_directory = $private_files_directory . '/temp'; + $this->public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); + $this->private_files_directory = $this->public_files_directory . '/private'; + $this->temp_files_directory = $this->private_files_directory . '/temp'; // Create the directories - file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY); - file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY); + file_prepare_directory($this->public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY); + file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); $this->generatedTestFiles = FALSE; // Log fatal errors. ini_set('log_errors', 1); - ini_set('error_log', $public_files_directory . '/error.log'); + ini_set('error_log', $this->public_files_directory . '/error.log'); // Set the test information for use in other parts of Drupal. $test_info = &$GLOBALS['drupal_test_info']; $test_info['test_run_id'] = $this->databasePrefix; $test_info['in_child_site'] = FALSE; + // Indicate the environment was set up correctly. + $this->setupEnvironment = TRUE; + } + + /** + * Sets up a Drupal site for running functional and integration tests. + * + * Generates a random database prefix and installs Drupal with the specified + * installation profile in DrupalWebTestCase::$profile into the + * prefixed database. Afterwards, installs any additional modules specified by + * the test. + * + * After installation all caches are flushed and several configuration values + * are reset to the values of the parent site executing the test, since the + * default values may be incompatible with the environment in which tests are + * being executed. + * + * @param ... + * List of modules to enable for the duration of the test. This can be + * either a single array or a variable number of string arguments. + * + * @see DrupalWebTestCase::prepareDatabasePrefix() + * @see DrupalWebTestCase::changeDatabasePrefix() + * @see DrupalWebTestCase::prepareEnvironment() + */ + protected function setUp() { + global $user, $language, $conf; + + // Create the database prefix for this test. + $this->prepareDatabasePrefix(); + + // Prepare the environment for running tests. + $this->prepareEnvironment(); + if (!$this->setupEnvironment) { + return FALSE; + } + + // Reset all statics and variables to perform tests in a clean environment. + $conf = array(); + drupal_static_reset(); + + // Change the database prefix. + // All static variables need to be reset before the database prefix is + // changed, since DrupalCacheArray implementations attempt to + // write back to persistent caches when they are destructed. + $this->changeDatabasePrefix(); + if (!$this->setupDatabasePrefix) { + return FALSE; + } + // Preset the 'install_profile' system variable, so the first call into // system_rebuild_module_data() (in drupal_install_system()) will register // the test's profile as a module. Without this, the installation profile of @@ -1334,15 +1439,16 @@ class DrupalWebTestCase extends DrupalTestCase { // profile's hook_install() and other hook implementations are never invoked. $conf['install_profile'] = $this->profile; + // Perform the actual Drupal installation. include_once DRUPAL_ROOT . '/includes/install.inc'; drupal_install_system(); $this->preloadRegistry(); // Set path variables. - variable_set('file_public_path', $public_files_directory); - variable_set('file_private_path', $private_files_directory); - variable_set('file_temporary_path', $temp_files_directory); + variable_set('file_public_path', $this->public_files_directory); + variable_set('file_private_path', $this->private_files_directory); + variable_set('file_temporary_path', $this->temp_files_directory); // Set the 'simpletest_parent_profile' variable to add the parent profile's // search path to the child site's search paths. @@ -1385,18 +1491,20 @@ class DrupalWebTestCase extends DrupalTestCase { // the installation process. drupal_cron_run(); - // Log in with a clean $user. - $this->originalUser = $user; + // Ensure that the session is not written to the new environment and replace + // the global $user session with uid 1 from the new test site. drupal_save_session(FALSE); + // Login as uid 1. $user = user_load(1); // Restore necessary variables. variable_set('install_task', 'done'); - variable_set('clean_url', $clean_url_original); + variable_set('clean_url', $this->originalCleanUrl); variable_set('site_mail', 'simpletest@example.com'); variable_set('date_default_timezone', date_default_timezone_get()); + // Set up English language. - unset($GLOBALS['conf']['language_default']); + unset($conf['language_default']); $language = language_default(); // Use the test mail class instead of the default mail handler class. @@ -1506,10 +1614,21 @@ class DrupalWebTestCase extends DrupalTestCase { // Delete temporary files directory. file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10)); - // Remove all prefixed tables (all the tables in the schema). - $schema = drupal_get_schema(NULL, TRUE); - foreach ($schema as $name => $table) { - db_drop_table($name); + // Remove all prefixed tables. + $tables = db_find_tables($this->databasePrefix . '%'); + $connection_info = Database::getConnectionInfo('default'); + $tables = db_find_tables($connection_info['default']['prefix']['default'] . '%'); + if (empty($tables)) { + $this->fail('Failed to find test tables to drop.'); + } + $prefix_length = strlen($connection_info['default']['prefix']['default']); + foreach ($tables as $table) { + if (db_drop_table(substr($table, $prefix_length))) { + unset($tables[$table]); + } + } + if (!empty($tables)) { + $this->fail('Failed to drop all prefixed tables.'); } // Get back to the original connection. @@ -1540,6 +1659,9 @@ class DrupalWebTestCase extends DrupalTestCase { // Rebuild caches. $this->refreshVariables(); + // Reset public files directory. + $GLOBALS['conf']['file_public_path'] = $this->originalFileDirectory; + // Reset language. $language = $this->originalLanguage; if ($this->originalLanguageDefault) { diff --git a/modules/simpletest/files/README.txt b/modules/simpletest/files/README.txt index c8f39ad33..680e7feab 100644 --- a/modules/simpletest/files/README.txt +++ b/modules/simpletest/files/README.txt @@ -1,4 +1,4 @@ - -These files are use in some tests that upload files or other operations were -a file is useful. These files are copied to the files directory as specified -in the site settings. Other tests files are generated in order to save space. +These files are useful in tests that upload files or otherwise need to +manipulate files, in which case they are copied to the files directory as +specified in the site settings. Dummy files can also be generated by tests in +order to save space. diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index a1ea1d798..682808719 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -54,8 +54,8 @@ files[] = tests/upgrade/upgrade.upload.test files[] = tests/upgrade/update.user.test files[] = tests/upgrade/upgrade.user.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/simpletest.js b/modules/simpletest/simpletest.js index 9cab26101..2199fede7 100644 --- a/modules/simpletest/simpletest.js +++ b/modules/simpletest/simpletest.js @@ -7,49 +7,50 @@ Drupal.behaviors.simpleTestMenuCollapse = { attach: function (context, settings) { var timeout = null; // Adds expand-collapse functionality. - $('div.simpletest-image').each(function () { - direction = settings.simpleTest[$(this).attr('id')].imageDirection; - $(this).html(settings.simpleTest.images[direction]); - }); - - // Adds group toggling functionality to arrow images. - $('div.simpletest-image').click(function () { - var trs = $(this).closest('tbody').children('.' + settings.simpleTest[this.id].testClass); + $('div.simpletest-image').once('simpletest-image', function () { + var $this = $(this); var direction = settings.simpleTest[this.id].imageDirection; - var row = direction ? trs.length - 1 : 0; + $this.html(settings.simpleTest.images[direction]); - // If clicked in the middle of expanding a group, stop so we can switch directions. - if (timeout) { - clearTimeout(timeout); - } + // Adds group toggling functionality to arrow images. + $this.click(function () { + var trs = $this.closest('tbody').children('.' + settings.simpleTest[this.id].testClass); + var direction = settings.simpleTest[this.id].imageDirection; + var row = direction ? trs.length - 1 : 0; - // Function to toggle an individual row according to the current direction. - // We set a timeout of 20 ms until the next row will be shown/hidden to - // create a sliding effect. - function rowToggle() { - if (direction) { - if (row >= 0) { - $(trs[row]).hide(); - row--; - timeout = setTimeout(rowToggle, 20); - } + // If clicked in the middle of expanding a group, stop so we can switch directions. + if (timeout) { + clearTimeout(timeout); } - else { - if (row < trs.length) { - $(trs[row]).removeClass('js-hide').show(); - row++; - timeout = setTimeout(rowToggle, 20); + + // Function to toggle an individual row according to the current direction. + // We set a timeout of 20 ms until the next row will be shown/hidden to + // create a sliding effect. + function rowToggle() { + if (direction) { + if (row >= 0) { + $(trs[row]).hide(); + row--; + timeout = setTimeout(rowToggle, 20); + } + } + else { + if (row < trs.length) { + $(trs[row]).removeClass('js-hide').show(); + row++; + timeout = setTimeout(rowToggle, 20); + } } } - } - // Kick-off the toggling upon a new click. - rowToggle(); + // Kick-off the toggling upon a new click. + rowToggle(); - // Toggle the arrow image next to the test group title. - $(this).html(settings.simpleTest.images[(direction ? 0 : 1)]); - settings.simpleTest[this.id].imageDirection = !direction; + // Toggle the arrow image next to the test group title. + $this.html(settings.simpleTest.images[(direction ? 0 : 1)]); + settings.simpleTest[this.id].imageDirection = !direction; + }); }); } }; @@ -60,7 +61,7 @@ Drupal.behaviors.simpleTestMenuCollapse = { */ Drupal.behaviors.simpleTestSelectAll = { attach: function (context, settings) { - $('td.simpletest-select-all').each(function () { + $('td.simpletest-select-all').once('simpletest-select-all', function () { var testCheckboxes = settings.simpleTest['simpletest-test-group-' + $(this).attr('id')].testNames; var groupCheckbox = $('<input type="checkbox" class="form-checkbox" id="' + $(this).attr('id') + '-select-all" />'); diff --git a/modules/simpletest/tests/actions.test b/modules/simpletest/tests/actions.test index 23587f0c5..4d58b59cc 100644 --- a/modules/simpletest/tests/actions.test +++ b/modules/simpletest/tests/actions.test @@ -97,7 +97,7 @@ class ActionLoopTestCase extends DrupalWebTestCase { // recursion level should be kept low enough to prevent the xdebug // infinite recursion protection mechanism from aborting the request. // See http://drupal.org/node/587634. - variable_set('actions_max_stack', mt_rand(3, 12)); + variable_set('actions_max_stack', 7); $this->triggerActions(); } diff --git a/modules/simpletest/tests/actions_loop_test.info b/modules/simpletest/tests/actions_loop_test.info index 26b9f4ab7..05d94ff38 100644 --- a/modules/simpletest/tests/actions_loop_test.info +++ b/modules/simpletest/tests/actions_loop_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/ajax_forms_test.info b/modules/simpletest/tests/ajax_forms_test.info index 28c76fa8b..f7daa105b 100644 --- a/modules/simpletest/tests/ajax_forms_test.info +++ b/modules/simpletest/tests/ajax_forms_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/ajax_test.info b/modules/simpletest/tests/ajax_test.info index de7e8292c..a6f17e5fe 100644 --- a/modules/simpletest/tests/ajax_test.info +++ b/modules/simpletest/tests/ajax_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/batch.test b/modules/simpletest/tests/batch.test index f668e5228..e41bc92be 100644 --- a/modules/simpletest/tests/batch.test +++ b/modules/simpletest/tests/batch.test @@ -341,8 +341,6 @@ class BatchPercentagesUnitTestCase extends DrupalUnitTestCase { '33' => array('total' => 3, 'current' => 1), // 2/3 is closer to 67% than to 66%. '67' => array('total' => 3, 'current' => 2), - // A full 3/3 should equal 100%. - '100' => array('total' => 3, 'current' => 3), // 1/199 should round up to 1%. '1' => array('total' => 199, 'current' => 1), // 198/199 should round down to 99%. diff --git a/modules/simpletest/tests/batch_test.info b/modules/simpletest/tests/batch_test.info index 3499be54c..681fc263f 100644 --- a/modules/simpletest/tests/batch_test.info +++ b/modules/simpletest/tests/batch_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/common_test.info b/modules/simpletest/tests/common_test.info index 0e301d62a..0b4cc4881 100644 --- a/modules/simpletest/tests/common_test.info +++ b/modules/simpletest/tests/common_test.info @@ -7,8 +7,8 @@ stylesheets[all][] = common_test.css stylesheets[print][] = common_test.print.css hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/common_test_cron_helper.info b/modules/simpletest/tests/common_test_cron_helper.info index 486984c35..1495c2159 100644 --- a/modules/simpletest/tests/common_test_cron_helper.info +++ b/modules/simpletest/tests/common_test_cron_helper.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/database_test.info b/modules/simpletest/tests/database_test.info index 4e9d5c6e5..33cfe605b 100644 --- a/modules/simpletest/tests/database_test.info +++ b/modules/simpletest/tests/database_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info index 5cdd71bfb..24b7bed8d 100644 --- a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info +++ b/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info index 474eddec9..5ebfd25ea 100644 --- a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info +++ b/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/simpletest/tests/entity_cache_test.info index bbd7d35b7..a70afd5b0 100644 --- a/modules/simpletest/tests/entity_cache_test.info +++ b/modules/simpletest/tests/entity_cache_test.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = entity_cache_test_dependency hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/simpletest/tests/entity_cache_test_dependency.info index 94119124a..157387a70 100644 --- a/modules/simpletest/tests/entity_cache_test_dependency.info +++ b/modules/simpletest/tests/entity_cache_test_dependency.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/entity_crud_hook_test.info b/modules/simpletest/tests/entity_crud_hook_test.info index e944705f6..22b784e2b 100644 --- a/modules/simpletest/tests/entity_crud_hook_test.info +++ b/modules/simpletest/tests/entity_crud_hook_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test index ddfd35433..6d77c508b 100644 --- a/modules/simpletest/tests/entity_query.test +++ b/modules/simpletest/tests/entity_query.test @@ -20,7 +20,7 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp(array('field_test')); + parent::setUp(array('node', 'field_test', 'entity_query_access_test', 'node_access_test')); field_test_create_bundle('bundle1'); field_test_create_bundle('bundle2'); @@ -1606,6 +1606,26 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { unset($_GET['order']); } + /** + * Tests EntityFieldQuery access on non-node entities. + */ + function testEntityFieldQueryAccess() { + // Test as a user with ability to bypass node access. + $privileged_user = $this->drupalCreateUser(array('bypass node access', 'access content')); + $this->drupalLogin($privileged_user); + $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']); + $this->assertText('Found entity', 'Returned access response with entities.'); + $this->drupalLogout(); + + // Test as a user that does not have ability to bypass node access or view + // all nodes. + $regular_user = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($regular_user); + $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']); + $this->assertText('Found entity', 'Returned access response with entities.'); + $this->drupalLogout(); + } + /** * Fetches the results of an EntityFieldQuery and compares. * @@ -1639,4 +1659,23 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->fail('Exception thrown: '. $e->getMessage()); } } + + /** + * Tests EFQ table prefixing with multiple conditions and an altered join. + * + * @see field_test_query_efq_table_prefixing_test_alter() + */ + function testTablePrefixing() { + $query = new EntityFieldQuery(); + $query = $query + ->entityCondition('entity_type', 'test_entity') + ->entityCondition('bundle', 'test_bundle') + ->entityCondition('entity_id', '1') + ->addTag('efq_table_prefixing_test'); + + $expected = array(array('test_entity', 1)); + + $this->assertEntityFieldQuery($query, $expected, 'An EntityFieldQuery returns the expected results when altered with an additional join on the base table.'); + } + } diff --git a/modules/simpletest/tests/entity_query_access_test.info b/modules/simpletest/tests/entity_query_access_test.info new file mode 100644 index 000000000..45b702bd4 --- /dev/null +++ b/modules/simpletest/tests/entity_query_access_test.info @@ -0,0 +1,12 @@ +name = "Entity query access test" +description = "Support module for checking entity query results." +package = Testing +version = VERSION +core = 7.x +hidden = TRUE + +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" +project = "drupal" +datestamp = "1343839327" + diff --git a/modules/simpletest/tests/entity_query_access_test.module b/modules/simpletest/tests/entity_query_access_test.module new file mode 100644 index 000000000..53641af5a --- /dev/null +++ b/modules/simpletest/tests/entity_query_access_test.module @@ -0,0 +1,54 @@ +<?php + +/** + * @file + * Helper module for testing EntityFieldQuery access on any type of entity. + */ + +/** + * Implements hook_menu(). + */ +function entity_query_access_test_menu() { + $items['entity-query-access/test/%'] = array( + 'title' => "Retrieve a sample of entity query access data", + 'page callback' => 'entity_query_access_test_sample_query', + 'page arguments' => array(2), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Returns the results from an example EntityFieldQuery. + */ +function entity_query_access_test_sample_query($field_name) { + global $user; + + // Simulate user does not have access to view all nodes. + $access = &drupal_static('node_access_view_all_nodes'); + $access[$user->uid] = FALSE; + + $query = new EntityFieldQuery(); + $query + ->entityCondition('entity_type', 'test_entity_bundle_key') + ->fieldCondition($field_name, 'value', 0, '>') + ->entityOrderBy('entity_id', 'ASC'); + $results = array( + 'items' => array(), + 'title' => t('EntityFieldQuery results'), + ); + foreach ($query->execute() as $entity_type => $entity_ids) { + foreach ($entity_ids as $entity_id => $entity_stub) { + $results['items'][] = format_string('Found entity of type @entity_type with id @entity_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id)); + } + } + if (count($results['items']) > 0) { + $output = theme('item_list', $results); + } + else { + $output = 'No results found with EntityFieldQuery.'; + } + return $output; +} diff --git a/modules/simpletest/tests/error_test.info b/modules/simpletest/tests/error_test.info index 80b6ca595..dcccf78d0 100644 --- a/modules/simpletest/tests/error_test.info +++ b/modules/simpletest/tests/error_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/file_test.info b/modules/simpletest/tests/file_test.info index 7bac12444..d95d91985 100644 --- a/modules/simpletest/tests/file_test.info +++ b/modules/simpletest/tests/file_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = file_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/filter_test.info b/modules/simpletest/tests/filter_test.info index 30fcbaa0f..b00109e11 100644 --- a/modules/simpletest/tests/filter_test.info +++ b/modules/simpletest/tests/filter_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index 2f5a9cd14..985abe31b 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -52,7 +52,7 @@ class FormsTestCase extends DrupalWebTestCase { $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); $elements['radios']['empty_values'] = $empty_arrays; - $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName()); + $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE); $elements['checkbox']['empty_values'] = $empty_checkbox; $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); @@ -173,6 +173,8 @@ class FormsTestCase extends DrupalWebTestCase { $this->assertNoFieldChecked('edit-radios-bar'); $this->assertNoFieldChecked('edit-radios-optional-foo'); $this->assertNoFieldChecked('edit-radios-optional-bar'); + $this->assertNoFieldChecked('edit-radios-optional-default-value-false-foo'); + $this->assertNoFieldChecked('edit-radios-optional-default-value-false-bar'); // Submit again with required fields set and verify that there are no // error messages. @@ -587,7 +589,7 @@ class FormValidationTestCase extends DrupalWebTestCase { */ function testValidateLimitErrors() { $edit = array( - 'test' => 'invalid', + 'test' => 'invalid', 'test_numeric_index[0]' => 'invalid', 'test_substring[foo]' => 'invalid', ); @@ -1167,6 +1169,51 @@ class FormStateValuesCleanTestCase extends DrupalWebTestCase { } } +/** + * Tests $form_state clearance with form elements having buttons. + */ +class FormStateValuesCleanAdvancedTestCase extends DrupalWebTestCase { + /** + * An image file path for uploading. + */ + protected $image; + + public static function getInfo() { + return array( + 'name' => 'Form state values clearance (advanced)', + 'description' => 'Test proper removal of submitted form values using form_state_values_clean() when having forms with elements containing buttons like "managed_file".', + 'group' => 'Form API', + ); + } + + function setUp() { + parent::setUp('form_test'); + } + + /** + * Tests form_state_values_clean(). + */ + function testFormStateValuesCleanAdvanced() { + + // Get an image for uploading. + $image_files = $this->drupalGetTestFiles('image'); + $this->image = current($image_files); + + // Check if the physical file is there. + $this->assertTrue(is_file($this->image->uri), t("The image file we're going to upload exists.")); + + // "Browse" for the desired file. + $edit = array('files[image]' => drupal_realpath($this->image->uri)); + + // Post the form. + $this->drupalPost('form_test/form-state-values-clean-advanced', $edit, t('Submit')); + + // Expecting a 200 HTTP code. + $this->assertResponse(200, t('Received a 200 response for posted test file.')); + $this->assertRaw(t('You WIN!'), t('Found the success message.')); + } +} + /** * Tests form rebuilding. * diff --git a/modules/simpletest/tests/form_test.info b/modules/simpletest/tests/form_test.info index 2b0b8ade2..691b663cc 100644 --- a/modules/simpletest/tests/form_test.info +++ b/modules/simpletest/tests/form_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index 43a6cbecb..e4ac77b12 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -99,6 +99,14 @@ function form_test_menu() { 'type' => MENU_CALLBACK, ); + $items['form_test/form-state-values-clean-advanced'] = array( + 'title' => 'Form state values clearance advanced test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_form_state_values_clean_advanced_form'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + $items['form-test/checkbox'] = array( 'title' => t('Form test'), 'page callback' => 'drupal_get_form', @@ -372,6 +380,12 @@ function form_test_validate_required_form($form, &$form_state) { '#title' => 'Radios (optional)', '#options' => $options, ); + $form['radios_optional_default_value_false'] = array( + '#type' => 'radios', + '#title' => 'Radios (optional, with a default value of FALSE)', + '#options' => $options, + '#default_value' => FALSE, + ); $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Submit'); return $form; @@ -872,6 +886,33 @@ function form_test_form_state_values_clean_form_submit($form, &$form_state) { exit; } +/** + * Form constructor for the form_state_values_clean() test. + */ +function form_test_form_state_values_clean_advanced_form($form, &$form_state) { + // Build an example form containing a managed file and a submit form element. + $form['image'] = array( + '#type' => 'managed_file', + '#title' => t('Image'), + '#upload_location' => 'public://', + '#default_value' => 0, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Submit'), + ); + return $form; +} + +/** + * Form submission handler for form_test_form_state_values_clean_advanced_form(). + */ +function form_test_form_state_values_clean_advanced_form_submit($form, &$form_state) { + form_state_values_clean($form_state); + print t('You WIN!'); + exit; +} + /** * Build a form to test a checkbox. */ diff --git a/modules/simpletest/tests/image_test.info b/modules/simpletest/tests/image_test.info index a7b747951..9082d46c9 100644 --- a/modules/simpletest/tests/image_test.info +++ b/modules/simpletest/tests/image_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test index a7fb8a484..b31fee6e3 100644 --- a/modules/simpletest/tests/menu.test +++ b/modules/simpletest/tests/menu.test @@ -242,12 +242,12 @@ class MenuRouterTestCase extends DrupalWebTestCase { $loggedInUser = $this->drupalCreateUser(array()); $this->drupalLogin($loggedInUser); - $this->DrupalGet('user/login'); + $this->drupalGet('user/login'); // Check that we got to 'user'. $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to q=user on accessing q=user/login")); // user/register should redirect to user/UID/edit. - $this->DrupalGet('user/register'); + $this->drupalGet('user/register'); $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to q=user/UID/edit on accessing q=user/register")); } diff --git a/modules/simpletest/tests/menu_test.info b/modules/simpletest/tests/menu_test.info index b3630c42d..04515d322 100644 --- a/modules/simpletest/tests/menu_test.info +++ b/modules/simpletest/tests/menu_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/module_test.info b/modules/simpletest/tests/module_test.info index 4e657f261..d3575f191 100644 --- a/modules/simpletest/tests/module_test.info +++ b/modules/simpletest/tests/module_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/path_test.info b/modules/simpletest/tests/path_test.info index 85298ec00..7a265dd90 100644 --- a/modules/simpletest/tests/path_test.info +++ b/modules/simpletest/tests/path_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/requirements1_test.info b/modules/simpletest/tests/requirements1_test.info index e26b5622d..16cb274be 100644 --- a/modules/simpletest/tests/requirements1_test.info +++ b/modules/simpletest/tests/requirements1_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/requirements2_test.info b/modules/simpletest/tests/requirements2_test.info index 6231da120..d2beee85c 100644 --- a/modules/simpletest/tests/requirements2_test.info +++ b/modules/simpletest/tests/requirements2_test.info @@ -7,8 +7,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/session_test.info b/modules/simpletest/tests/session_test.info index 265961635..f32fd4bec 100644 --- a/modules/simpletest/tests/session_test.info +++ b/modules/simpletest/tests/session_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/system_dependencies_test.info b/modules/simpletest/tests/system_dependencies_test.info index ec796e629..177c729e6 100644 --- a/modules/simpletest/tests/system_dependencies_test.info +++ b/modules/simpletest/tests/system_dependencies_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = _missing_dependency -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info index 623be8da7..7f80147e7 100644 --- a/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info +++ b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = system_incompatible_core_version_test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/system_incompatible_core_version_test.info b/modules/simpletest/tests/system_incompatible_core_version_test.info index 598534ee8..8b549b1a3 100644 --- a/modules/simpletest/tests/system_incompatible_core_version_test.info +++ b/modules/simpletest/tests/system_incompatible_core_version_test.info @@ -5,8 +5,8 @@ version = VERSION core = 5.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info index ef04a9954..f49369269 100644 --- a/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info +++ b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info @@ -7,8 +7,8 @@ hidden = TRUE ; system_incompatible_module_version_test declares version 1.0 dependencies[] = system_incompatible_module_version_test (>2.0) -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/system_incompatible_module_version_test.info b/modules/simpletest/tests/system_incompatible_module_version_test.info index b13677d68..f36815cb6 100644 --- a/modules/simpletest/tests/system_incompatible_module_version_test.info +++ b/modules/simpletest/tests/system_incompatible_module_version_test.info @@ -5,8 +5,8 @@ version = 1.0 core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/system_test.info b/modules/simpletest/tests/system_test.info index f4b5e5dff..664f6a6cc 100644 --- a/modules/simpletest/tests/system_test.info +++ b/modules/simpletest/tests/system_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = system_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/taxonomy_test.info b/modules/simpletest/tests/taxonomy_test.info index fde85316e..1d6360853 100644 --- a/modules/simpletest/tests/taxonomy_test.info +++ b/modules/simpletest/tests/taxonomy_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = taxonomy -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index ba6440028..27a8e47b9 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -124,6 +124,37 @@ class ThemeTestCase extends DrupalWebTestCase { module_enable(array('theme_test'), FALSE); $this->assertIdentical(theme('theme_test_foo', array('foo' => 'c')), 'c', 'The theme registry contains theme_test_foo again after re-enabling the module.'); } + + /** + * Test the list_themes() function. + */ + function testListThemes() { + $themes = list_themes(); + // Check if drupal_theme_access() retrieves enabled themes properly from list_themes(). + $this->assertTrue(drupal_theme_access('test_theme'), t('Enabled theme detected')); + // Check if list_themes() returns disabled themes. + $this->assertTrue(array_key_exists('test_basetheme', $themes), t('Disabled theme detected')); + // Check for base theme and subtheme lists. + $base_theme_list = array('test_basetheme' => 'Theme test base theme'); + $sub_theme_list = array('test_subtheme' => 'Theme test subtheme'); + $this->assertIdentical($themes['test_basetheme']->sub_themes, $sub_theme_list, t('Base theme\'s object includes list of subthemes.')); + $this->assertIdentical($themes['test_subtheme']->base_themes, $base_theme_list, t('Subtheme\'s object includes list of base themes.')); + // Check for theme engine in subtheme. + $this->assertIdentical($themes['test_subtheme']->engine, 'phptemplate', t('Subtheme\'s object includes the theme engine.')); + // Check for theme engine prefix. + $this->assertIdentical($themes['test_basetheme']->prefix, 'phptemplate', t('Base theme\'s object includes the theme engine prefix.')); + $this->assertIdentical($themes['test_subtheme']->prefix, 'phptemplate', t('Subtheme\'s object includes the theme engine prefix.')); + } + + /** + * Test the theme_get_setting() function. + */ + function testThemeGetSetting() { + $GLOBALS['theme_key'] = 'test_theme'; + $this->assertIdentical(theme_get_setting('theme_test_setting'), 'default value', t('theme_get_setting() uses the default theme automatically.')); + $this->assertNotEqual(theme_get_setting('subtheme_override', 'test_basetheme'), theme_get_setting('subtheme_override', 'test_subtheme'), t('Base theme\'s default settings values can be overridden by subtheme.')); + $this->assertIdentical(theme_get_setting('basetheme_only', 'test_subtheme'), 'base theme value', t('Base theme\'s default settings values are inherited by subtheme.')); + } } /** diff --git a/modules/simpletest/tests/theme_test.info b/modules/simpletest/tests/theme_test.info index c7e6ac1b7..341e921d1 100644 --- a/modules/simpletest/tests/theme_test.info +++ b/modules/simpletest/tests/theme_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module index a9bd2ad24..61a12bb70 100644 --- a/modules/simpletest/tests/theme_test.module +++ b/modules/simpletest/tests/theme_test.module @@ -25,6 +25,8 @@ function theme_test_theme($existing, $type, $theme, $path) { */ function theme_test_system_theme_info() { $themes['test_theme'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme/test_theme.info'; + $themes['test_basetheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_basetheme/test_basetheme.info'; + $themes['test_subtheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_subtheme/test_subtheme.info'; return $themes; } diff --git a/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info b/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info new file mode 100644 index 000000000..8ebbe2c90 --- /dev/null +++ b/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info @@ -0,0 +1,13 @@ +name = Theme test base theme +description = Test theme which acts as a base theme for other test subthemes. +core = 7.x +hidden = TRUE + +settings[basetheme_only] = base theme value +settings[subtheme_override] = base theme value + +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" +project = "drupal" +datestamp = "1343839327" + diff --git a/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info b/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info new file mode 100644 index 000000000..9e65f1f3d --- /dev/null +++ b/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info @@ -0,0 +1,13 @@ +name = Theme test subtheme +description = Test theme which uses test_basetheme as the base theme. +core = 7.x +base theme = test_basetheme +hidden = TRUE + +settings[subtheme_override] = subtheme value + +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" +project = "drupal" +datestamp = "1343839327" + diff --git a/modules/simpletest/tests/themes/test_theme/test_theme.info b/modules/simpletest/tests/themes/test_theme/test_theme.info index a27c73d2b..69711913b 100644 --- a/modules/simpletest/tests/themes/test_theme/test_theme.info +++ b/modules/simpletest/tests/themes/test_theme/test_theme.info @@ -15,8 +15,10 @@ hidden = TRUE ; file within the theme folder. stylesheets[all][] = system.base.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +settings[theme_test_setting] = default value + +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/update_script_test.info b/modules/simpletest/tests/update_script_test.info index 33ec1a056..85021b464 100644 --- a/modules/simpletest/tests/update_script_test.info +++ b/modules/simpletest/tests/update_script_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/update_test_1.info b/modules/simpletest/tests/update_test_1.info index 9508b38e7..879aaddd2 100644 --- a/modules/simpletest/tests/update_test_1.info +++ b/modules/simpletest/tests/update_test_1.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/update_test_2.info b/modules/simpletest/tests/update_test_2.info index 9508b38e7..879aaddd2 100644 --- a/modules/simpletest/tests/update_test_2.info +++ b/modules/simpletest/tests/update_test_2.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/update_test_3.info b/modules/simpletest/tests/update_test_3.info index 9508b38e7..879aaddd2 100644 --- a/modules/simpletest/tests/update_test_3.info +++ b/modules/simpletest/tests/update_test_3.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test index 172f30e69..9df8ec779 100644 --- a/modules/simpletest/tests/upgrade/upgrade.test +++ b/modules/simpletest/tests/upgrade/upgrade.test @@ -71,7 +71,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { } /** - * Override of DrupalWebTestCase::setUp() specialized for upgrade testing. + * Overrides DrupalWebTestCase::setUp() for upgrade testing. + * + * @see DrupalWebTestCase::prepareDatabasePrefix() + * @see DrupalWebTestCase::changeDatabasePrefix() + * @see DrupalWebTestCase::prepareEnvironment() */ protected function setUp() { // We are going to set a missing zlib requirement property for usage @@ -93,55 +97,33 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { $this->loadedModules = module_list(); - // Generate a temporary prefixed database to ensure that tests have a clean starting point. - $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); - db_update('simpletest_test_id') - ->fields(array('last_prefix' => $this->databasePrefix)) - ->condition('test_id', $this->testId) - ->execute(); + // Create the database prefix for this test. + $this->prepareDatabasePrefix(); - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('default'); - Database::renameConnection('default', 'simpletest_original_default'); - foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = array( - 'default' => $value['prefix']['default'] . $this->databasePrefix, - ); + // Prepare the environment for running tests. + $this->prepareEnvironment(); + if (!$this->setupEnvironment) { + return FALSE; } - Database::addConnectionInfo('default', 'default', $connection_info['default']); - // Store necessary current values before switching to prefixed database. - $this->originalLanguage = $language; - $this->originalLanguageDefault = variable_get('language_default'); - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); - $this->originalProfile = drupal_get_profile(); - $clean_url_original = variable_get('clean_url', 0); + // Reset all statics and variables to perform tests in a clean environment. + $conf = array(); + drupal_static_reset(); + + // Change the database prefix. + // All static variables need to be reset before the database prefix is + // changed, since DrupalCacheArray implementations attempt to + // write back to persistent caches when they are destructed. + $this->changeDatabasePrefix(); + if (!$this->setupDatabasePrefix) { + return FALSE; + } // Unregister the registry. // This is required to make sure that the database layer works properly. spl_autoload_unregister('drupal_autoload_class'); spl_autoload_unregister('drupal_autoload_interface'); - // Create test directories ahead of installation so fatal errors and debug - // information can be logged during installation process. - // Use mock files directories with the same prefix as the database. - $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - $private_files_directory = $public_files_directory . '/private'; - $temp_files_directory = $private_files_directory . '/temp'; - - // Create the directories. - file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - file_prepare_directory($private_files_directory, FILE_CREATE_DIRECTORY); - file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY); - $this->generatedTestFiles = FALSE; - - // Log fatal errors. - ini_set('log_errors', 1); - ini_set('error_log', $public_files_directory . '/error.log'); - - // Reset all statics and variables to perform tests in a clean environment. - $conf = array(); - // Load the database from the portable PHP dump. // The files may be gzipped. foreach ($this->databaseDumpFiles as $file) { @@ -152,85 +134,29 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { } // Set path variables. - $this->variable_set('file_public_path', $public_files_directory); - $this->variable_set('file_private_path', $private_files_directory); - $this->variable_set('file_temporary_path', $temp_files_directory); + $this->variable_set('file_public_path', $this->public_files_directory); + $this->variable_set('file_private_path', $this->private_files_directory); + $this->variable_set('file_temporary_path', $this->temp_files_directory); $this->pass('Finished loading the dump.'); - // Load user 1. - $this->originalUser = $user; + // Ensure that the session is not written to the new environment and replace + // the global $user session with uid 1 from the new test site. drupal_save_session(FALSE); + // Login as uid 1. $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject(); // Generate and set a D6-compatible session cookie. $this->prepareD7Session(); // Restore necessary variables. - $this->variable_set('clean_url', $clean_url_original); + $this->variable_set('clean_url', $this->originalCleanUrl); $this->variable_set('site_mail', 'simpletest@example.com'); drupal_set_time_limit($this->timeLimit); $this->setup = TRUE; } - /** - * Override of DrupalWebTestCase::tearDown() specialized for upgrade testing. - */ - protected function tearDown() { - global $user, $language; - - if (!$this->zlibInstalled) { - parent::tearDown(); - return; - } - - // In case a fatal error occurred that was not in the test process read the - // log to pick up any fatal errors. - simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); - - // Delete temporary files directory. - file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10)); - - // Get back to the original connection. - Database::removeConnection('default'); - Database::renameConnection('simpletest_original_default', 'default'); - - // Remove all prefixed tables. - $tables = db_find_tables($this->databasePrefix . '%'); - foreach ($tables as $table) { - db_drop_table($table); - } - - // Return the user to the original one. - $user = $this->originalUser; - drupal_save_session(TRUE); - - // Ensure that internal logged in variable and cURL options are reset. - $this->loggedInUser = FALSE; - $this->additionalCurlOptions = array(); - - // Reload module list and implementations to ensure that test module hooks - // aren't called after tests. - module_list(TRUE); - module_implements('', FALSE, TRUE); - - // Reset the Field API. - field_cache_clear(); - - // Rebuild caches. - parent::refreshVariables(); - - // Reset language. - $language = $this->originalLanguage; - if ($this->originalLanguageDefault) { - $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault; - } - - // Close the CURL handler. - $this->curlClose(); - } - /** * Specialized variable_set() that works even if the child site is not upgraded. * diff --git a/modules/simpletest/tests/url_alter_test.info b/modules/simpletest/tests/url_alter_test.info index efeb1d842..00e73ab7a 100644 --- a/modules/simpletest/tests/url_alter_test.info +++ b/modules/simpletest/tests/url_alter_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/simpletest/tests/xmlrpc_test.info b/modules/simpletest/tests/xmlrpc_test.info index ba2aa341a..36cdc17a8 100644 --- a/modules/simpletest/tests/xmlrpc_test.info +++ b/modules/simpletest/tests/xmlrpc_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/statistics/statistics.admin.inc b/modules/statistics/statistics.admin.inc index 6606b8b95..415fd0054 100644 --- a/modules/statistics/statistics.admin.inc +++ b/modules/statistics/statistics.admin.inc @@ -2,11 +2,18 @@ /** * @file - * Admin page callbacks for the statistics module. + * Admin page callbacks for the Statistics module. */ /** - * Menu callback; presents the "recent hits" page. + * Page callback: Displays the "recent hits" page. + * + * This displays the pages with recent hits in a given time interval that + * haven't been flushed yet. The flush interval is set on the statistics + * settings form, but is dependent on cron running. + * + * @return + * A render array containing information about the most recent hits. */ function statistics_recent_hits() { $header = array( @@ -45,7 +52,14 @@ function statistics_recent_hits() { } /** - * Menu callback; presents the "top pages" page. + * Page callback: Displays statistics for the "top pages" (most accesses). + * + * This displays the pages with the most hits (the "top pages") within a given + * time period that haven't been flushed yet. The flush interval is set on the + * statistics settings form, but is dependent on cron running. + * + * @return + * A render array containing information about the the top pages. */ function statistics_top_pages() { $header = array( @@ -57,7 +71,8 @@ function statistics_top_pages() { $query = db_select('accesslog', 'a', array('target' => 'slave'))->extend('PagerDefault')->extend('TableSort'); $query->addExpression('COUNT(path)', 'hits'); - // MAX(title) avoids having empty node titles which otherwise causes duplicates in the top pages list + // MAX(title) avoids having empty node titles which otherwise causes + // duplicates in the top pages list. $query->addExpression('MAX(title)', 'title'); $query->addExpression('AVG(timer)', 'average_time'); $query->addExpression('SUM(timer)', 'total_time'); @@ -90,7 +105,14 @@ function statistics_top_pages() { } /** - * Menu callback; presents the "top visitors" page. + * Page callback: Displays the "top visitors" page. + * + * This displays the pages with the top number of visitors in a given time + * interval that haven't been flushed yet. The flush interval is set on the + * statistics settings form, but is dependent on cron running. + * + * @return + * A render array containing the top visitors information. */ function statistics_top_visitors() { @@ -143,7 +165,14 @@ function statistics_top_visitors() { } /** - * Menu callback; presents the "referrer" page. + * Page callback: Displays the "top referrers" in the access logs. + * + * This displays the pages with the top referrers in a given time interval that + * haven't been flushed yet. The flush interval is set on the statistics + * settings form, but is dependent on cron running. + * + * @return + * A render array containing the top referrers information. */ function statistics_top_referrers() { drupal_set_title(t('Top referrers in the past %interval', array('%interval' => format_interval(variable_get('statistics_flush_accesslog_timer', 259200)))), PASS_THROUGH); @@ -189,7 +218,14 @@ function statistics_top_referrers() { } /** - * Menu callback; Displays recent page accesses. + * Page callback: Gathers page access statistics suitable for rendering. + * + * @param $aid + * The unique accesslog ID. + * + * @return + * A render array containing page access statistics. If information for the + * page was not found, drupal_not_found() is called. */ function statistics_access_log($aid) { $access = db_query('SELECT a.*, u.name FROM {accesslog} a LEFT JOIN {users} u ON a.uid = u.uid WHERE aid = :aid', array(':aid' => $aid))->fetch(); @@ -233,7 +269,7 @@ function statistics_access_log($aid) { } /** - * Form builder; Configure access logging. + * Form constructor for the statistics administration form. * * @ingroup forms * @see system_settings_form() diff --git a/modules/statistics/statistics.info b/modules/statistics/statistics.info index baff9b842..42af6abd5 100644 --- a/modules/statistics/statistics.info +++ b/modules/statistics/statistics.info @@ -6,8 +6,8 @@ core = 7.x files[] = statistics.test configure = admin/config/system/statistics -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/statistics/statistics.install b/modules/statistics/statistics.install index d5855a77f..b85743553 100644 --- a/modules/statistics/statistics.install +++ b/modules/statistics/statistics.install @@ -2,7 +2,7 @@ /** * @file - * Install, update and uninstall functions for the statistics module. + * Install, update, and uninstall functions for the Statistics module. */ /** @@ -154,5 +154,5 @@ function statistics_update_7000() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/statistics/statistics.module b/modules/statistics/statistics.module index 89cda6dcf..81d24b755 100644 --- a/modules/statistics/statistics.module +++ b/modules/statistics/statistics.module @@ -2,7 +2,7 @@ /** * @file - * Logs access statistics for your site. + * Logs and displays access statistics for a site. */ /** @@ -45,7 +45,7 @@ function statistics_help($path, $arg) { /** * Implements hook_exit(). * - * This is where statistics are gathered on page accesses. + * Gathers statistics for page accesses. */ function statistics_exit() { global $user; @@ -249,20 +249,20 @@ function statistics_cron() { } /** - * Returns all time or today top or last viewed node(s). + * Returns the most viewed content of all time, today, or the last-viewed node. * * @param $dbfield - * one of - * - 'totalcount': top viewed content of all time. - * - 'daycount': top viewed content for today. - * - 'timestamp': last viewed node. - * + * The database field to use, one of: + * - 'totalcount': Integer that shows the top viewed content of all time. + * - 'daycount': Integer that shows the top viewed content for today. + * - 'timestamp': Integer that shows only the last viewed node. * @param $dbrows - * number of rows to be returned. + * The number of rows to be returned. * - * @return - * A query result containing n.nid, n.title, u.uid, u.name of the selected node(s) - * or FALSE if the query could not be executed correctly. + * @return SelectQuery|FALSE + * A query result containing the node ID, title, user ID that owns the node, + * and the username for the selected node(s), or FALSE if the query could not + * be executed correctly. */ function statistics_title_list($dbfield, $dbrows) { if (in_array($dbfield, array('totalcount', 'daycount', 'timestamp'))) { @@ -288,14 +288,15 @@ function statistics_title_list($dbfield, $dbrows) { * Retrieves a node's "view statistics". * * @param $nid - * node ID + * The node ID. * * @return - * An array with three entries: [0]=totalcount, [1]=daycount, [2]=timestamp - * - totalcount: count of the total number of times that node has been viewed. - * - daycount: count of the total number of times that node has been viewed "today". - * For the daycount to be reset, cron must be enabled. - * - timestamp: timestamp of when that node was last viewed. + * An associative array containing: + * - totalcount: Integer for the total number of times the node has been + * viewed. + * - daycount: Integer for the total number of times the node has been viewed + * "today". For the daycount to be reset, cron must be enabled. + * - timestamp: Integer for the timestamp of when the node was last viewed. */ function statistics_get($nid) { @@ -374,8 +375,15 @@ function statistics_block_view($delta = '') { } /** - * It is possible to adjust the width of columns generated by the - * statistics module. + * Generates a link to a path, truncating the displayed text to a given width. + * + * @param $path + * The path to generate the link for. + * @param $width + * The width to set the displayed text of the path. + * + * @return + * A string as a link, truncated to the width, linked to the given $path. */ function _statistics_link($path, $width = 35) { $title = drupal_get_path_alias($path); @@ -383,6 +391,17 @@ function _statistics_link($path, $width = 35) { return l($title, $path); } +/** + * Formats an item for display, including both the item title and the link. + * + * @param $title + * The text to link to a path; will be truncated to a maximum width of 35. + * @param $path + * The path to link to; will default to '/'. + * + * @return + * An HTML string with $title linked to the $path. + */ function _statistics_format_item($title, $path) { $path = ($path ? $path : '/'); $output = ($title ? "$title<br />" : ''); diff --git a/modules/statistics/statistics.pages.inc b/modules/statistics/statistics.pages.inc index bb31f9838..8bd9712f6 100644 --- a/modules/statistics/statistics.pages.inc +++ b/modules/statistics/statistics.pages.inc @@ -2,9 +2,16 @@ /** * @file - * User page callbacks for the statistics module. + * User page callbacks for the Statistics module. */ +/** + * Page callback: Displays statistics for a node. + * + * @return + * A render array containing node statistics. If information for the node was + * not found, this will deliver a page not found error via drupal_not_found(). + */ function statistics_node_tracker() { if ($node = node_load(arg(1))) { @@ -52,6 +59,13 @@ function statistics_node_tracker() { } } +/** + * Page callback: Displays statistics for a user. + * + * @return array + * A render array containing user statistics. If information for the user was + * not found, this will deliver a page not found error via drupal_not_found(). + */ function statistics_user_tracker() { if ($account = user_load(arg(1))) { diff --git a/modules/statistics/statistics.test b/modules/statistics/statistics.test index f12490acf..a40ecc80e 100644 --- a/modules/statistics/statistics.test +++ b/modules/statistics/statistics.test @@ -2,11 +2,11 @@ /** * @file - * Tests for statistics.module. + * Tests for the Statistics module. */ /** - * Sets up a base class for the Statistics module. + * Defines a base class for testing the Statistics module. */ class StatisticsTestCase extends DrupalWebTestCase { @@ -46,10 +46,10 @@ class StatisticsTestCase extends DrupalWebTestCase { } /** - * Tests that logging via statistics_exit() works for cached and uncached pages. + * Tests that logging via statistics_exit() works for all pages. * - * Subclass DrupalWebTestCase rather than StatisticsTestCase, because we want - * to test requests from an anonymous user. + * We subclass DrupalWebTestCase rather than StatisticsTestCase, because we + * want to test requests from an anonymous user. */ class StatisticsLoggingTestCase extends DrupalWebTestCase { public static function getInfo() { @@ -282,10 +282,24 @@ class StatisticsBlockVisitorsTestCase extends StatisticsTestCase { } /** - * Test statistics administration screen. + * Tests the statistics administration screen. */ class StatisticsAdminTestCase extends DrupalWebTestCase { + + /** + * A user that has permission to administer and access statistics. + * + * @var object|FALSE + * + * A fully loaded user object, or FALSE if user creation failed. + */ protected $privileged_user; + + /** + * A page node for which to check access statistics. + * + * @var object + */ protected $test_node; public static function getInfo() { @@ -422,7 +436,7 @@ class StatisticsAdminTestCase extends DrupalWebTestCase { } /** - * Test statistics token replacement in strings. + * Tests statistics token replacement in strings. */ class StatisticsTokenReplaceTestCase extends StatisticsTestCase { public static function getInfo() { diff --git a/modules/syslog/syslog.info b/modules/syslog/syslog.info index 01d2cb4e7..783b2047c 100644 --- a/modules/syslog/syslog.info +++ b/modules/syslog/syslog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = syslog.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/system/image.gd.inc b/modules/system/image.gd.inc index 39f86dc30..91c0b05ff 100644 --- a/modules/system/image.gd.inc +++ b/modules/system/image.gd.inc @@ -6,7 +6,7 @@ */ /** - * @ingroup image + * @addtogroup image * @{ */ @@ -363,5 +363,5 @@ function image_gd_get_info(stdClass $image) { } /** - * @} End of "ingroup image". + * @} End of "addtogroup image". */ diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index e9682e7dd..23a975b0c 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -400,7 +400,7 @@ function system_theme_settings($form, &$form_state, $key = '') { // Default settings are defined in theme_get_setting() in includes/theme.inc if ($key) { $var = 'theme_' . $key . '_settings'; - $themes = system_rebuild_theme_data(); + $themes = list_themes(); $features = $themes[$key]->info['features']; } else { diff --git a/modules/system/system.api.php b/modules/system/system.api.php index e28386997..79ac8f169 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -97,6 +97,18 @@ function hook_hook_info_alter(&$hooks) { * instead specify a callback function here, which will be called to * determine the entity label. See also the entity_label() function, which * implements this logic. + * - language callback: (optional) A function taking an entity and an entity + * type as arguments and returning a language code. In most situations, when + * needing to determine this value, inspecting a property named after the + * 'language' element of the 'entity keys' should be enough. The language + * callback is meant to be used primarily for temporary alterations of the + * property value: entity-defining modules are encouraged to always define a + * language property, instead of using the callback as main entity language + * source. In fact not having a language property defined is likely to + * prevent an entity from being queried by language. Moreover, given that + * entity_language() is not necessarily used everywhere it would be + * appropriate, modules implementing the language callback should be aware + * that this might not be always called. * - fieldable: Set to TRUE if you want your entity type to accept fields * being attached to it. * - translation: An associative array of modules registered as field @@ -123,6 +135,13 @@ function hook_hook_info_alter(&$hooks) { * 'subject' should be specified here. If complex logic is required to * build the label, a 'label callback' should be defined instead (see * the 'label callback' section above for details). + * - language: The name of the property, typically 'language', that contains + * the language code representing the language the entity has been created + * in. This value may be changed when editing the entity and represents + * the language its textual components are supposed to have. If no + * language property is available, the 'language callback' may be used + * instead. This entry can be omitted if the entities of this type are not + * language-aware. * - bundle keys: An array describing how the Field API can extract the * information it needs from the bundle objects for this type. This entry * is required if the 'path' provided in the 'bundles'/'admin' section @@ -195,6 +214,7 @@ function hook_entity_info() { 'id' => 'nid', 'revision' => 'vid', 'bundle' => 'type', + 'language' => 'language', ), 'bundle keys' => array( 'bundle' => 'type', @@ -1030,7 +1050,7 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * is invoked to retrieve a value that is used in the path in place of the * wildcard. A good example is user.module, which defines * user_uid_optional_to_arg() (corresponding to the menu entry - * 'user/%user_uid_optional'). This function returns the user ID of the + * 'tracker/%user_uid_optional'). This function returns the user ID of the * current user. * * The _to_arg() function will get called with three arguments: @@ -1797,8 +1817,8 @@ function hook_forms($form_id, $args) { * used to set up global parameters that are needed later in the request. * * Only use this hook if your code must run even for cached page views. This - * hook is called before modules or most include files are loaded into memory. - * It happens while Drupal is still in bootstrap mode. + * hook is called before the theme, modules, or most include files are loaded + * into memory. It happens while Drupal is still in bootstrap mode. * * @see hook_init() */ @@ -1813,7 +1833,8 @@ function hook_boot() { * * This hook is run at the beginning of the page request. It is typically * used to set up global parameters that are needed later in the request. - * When this hook is called, all modules are already loaded in memory. + * When this hook is called, the theme and all modules are already loaded in + * memory. * * This hook is not run on cached pages. * @@ -2343,6 +2364,7 @@ function hook_xmlrpc_alter(&$methods) { * - type: The type of message for this entry. * - user: The user object for the user who was logged in when the event * happened. + * - uid: The user ID for the user who was logged in when the event happened. * - request_uri: The request URI for the page the event happened in. * - referer: The page that referred the user to the page where the event * occurred. @@ -2409,7 +2431,7 @@ function hook_watchdog(array $log_entry) { '@ip' => $log_entry['ip'], '@request_uri' => $log_entry['request_uri'], '@referer_uri' => $log_entry['referer'], - '@uid' => $log_entry['user']->uid, + '@uid' => $log_entry['uid'], '@name' => $log_entry['user']->name, '@link' => strip_tags($log_entry['link']), '@message' => strip_tags($log_entry['message']), @@ -3226,33 +3248,30 @@ function hook_install() { /** * Perform a single update. * - * For each patch which requires a database change add a new hook_update_N() - * which will be called by update.php. The database updates are numbered - * sequentially according to the version of Drupal you are compatible with. - * - * Schema updates should adhere to the Schema API: - * @link http://drupal.org/node/150215 http://drupal.org/node/150215 @endlink - * - * Database updates consist of 3 parts: - * - 1 digit for Drupal core compatibility - * - 1 digit for your module's major release version (e.g. is this the 5.x-1.* (1) or 5.x-2.* (2) series of your module?) - * - 2 digits for sequential counting starting with 00 - * - * The 2nd digit should be 0 for initial porting of your module to a new Drupal - * core API. + * For each change that requires one or more actions to be performed when + * updating a site, add a new hook_update_N(), which will be called by + * update.php. The documentation block preceding this function is stripped of + * newlines and used as the description for the update on the pending updates + * task list. Schema updates should adhere to the + * @link http://drupal.org/node/150215 Schema API. @endlink + * + * Implementations of hook_update_N() are named (module name)_update_(number). + * The numbers are composed of three parts: + * - 1 digit for Drupal core compatibility. + * - 1 digit for your module's major release version (e.g., is this the 7.x-1.* + * (1) or 7.x-2.* (2) series of your module?). This digit should be 0 for + * initial porting of your module to a new Drupal core API. + * - 2 digits for sequential counting, starting with 00. * * Examples: - * - mymodule_update_5200() - * - This is the first update to get the database ready to run mymodule 5.x-2.*. - * - mymodule_update_6000() - * - This is the required update for mymodule to run with Drupal core API 6.x. - * - mymodule_update_6100() - * - This is the first update to get the database ready to run mymodule 6.x-1.*. - * - mymodule_update_6200() - * - This is the first update to get the database ready to run mymodule 6.x-2.*. - * Users can directly update from 5.x-2.* to 6.x-2.* and they get all 60XX - * and 62XX updates, but not 61XX updates, because those reside in the - * 6.x-1.x branch only. + * - mymodule_update_7000(): This is the required update for mymodule to run + * with Drupal core API 7.x when upgrading from Drupal core API 6.x. + * - mymodule_update_7100(): This is the first update to get the database ready + * to run mymodule 7.x-1.*. + * - mymodule_update_7200(): This is the first update to get the database ready + * to run mymodule 7.x-2.*. Users can directly update from 6.x-2.* to 7.x-2.* + * and they get all 70xx and 72xx updates, but not 71xx updates, because + * those reside in the 7.x-1.x branch only. * * A good rule of thumb is to remove updates older than two major releases of * Drupal. See hook_update_last_removed() to notify Drupal about the removals. @@ -3271,8 +3290,8 @@ function hook_install() { * information between successive calls, and the $sandbox['#finished'] value * to provide feedback regarding completion level. * - * See the batch operations page for more information on how to use the batch API: - * @link http://drupal.org/node/180528 http://drupal.org/node/180528 @endlink + * See the batch operations page for more information on how to use the + * @link http://drupal.org/node/180528 Batch API. @endlink * * @param $sandbox * Stores information for multipass updates. See above for more information. @@ -3283,9 +3302,14 @@ function hook_install() { * reason, it will throw a PDOException. * * @return - * Optionally update hooks may return a translated string that will be displayed - * to the user. If no message is returned, no message will be presented to the - * user. + * Optionally, update hooks may return a translated string that will be + * displayed to the user after the update has completed. If no message is + * returned, no message will be presented to the user. + * + * @see batch + * @see schemaapi + * @see hook_update_last_removed() + * @see update_get_update_list() */ function hook_update_N(&$sandbox) { // For non-multipass updates, the signature can simply be; diff --git a/modules/system/system.base.css b/modules/system/system.base.css index bf6dd9f71..65383dd34 100644 --- a/modules/system/system.base.css +++ b/modules/system/system.base.css @@ -27,6 +27,7 @@ color: #000; cursor: default; white-space: pre; + zoom: 1; /* IE7 */ } /* Animated throbber */ html.js input.form-autocomplete { diff --git a/modules/system/system.info b/modules/system/system.info index 7a3f8ebd4..da3fc753d 100644 --- a/modules/system/system.info +++ b/modules/system/system.info @@ -12,8 +12,8 @@ files[] = system.test required = TRUE configure = admin/config/system -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/system/system.install b/modules/system/system.install index c0300000e..1161d6cdd 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -2952,6 +2952,16 @@ function system_update_7069() { variable_del('site_offline'); } +/** + * @} End of "defgroup updates-6.x-to-7.x". + * The next series of updates should start at 8000. + */ + +/** + * @defgroup updates-7.x-extra Extra system updates for 7.x + * @{ + */ + /** * Remove the obsolete 'drupal_badge_color' and 'drupal_badge_size' variables. */ @@ -2982,16 +2992,6 @@ function system_update_7072() { variable_del('site_offline_message'); } -/** - * @} End of "defgroup updates-6.x-to-7.x" - * The next series of updates should start at 8000. - */ - -/** - * @defgroup updates-7.x-extra Extra system updates for 7.x - * @{ - */ - /** * Add binary to {file_managed}, in case system_update_7034() was run without * it. @@ -3016,6 +3016,15 @@ function system_update_7073() { } /** - * @} End of "defgroup updates-7.x-extra" + * This update has been removed and will not run. + */ +function system_update_7074() { + // This update function previously converted menu_links query strings to + // arrays. It has been removed for now due to incompatibility with + // PostgreSQL. +} + +/** + * @} End of "defgroup updates-7.x-extra". * The next series of updates should start at 8000. */ diff --git a/modules/system/system.module b/modules/system/system.module index 072850e01..9897fb25e 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -2023,6 +2023,10 @@ function system_block_info() { 'info' => t('Main page content'), // Cached elsewhere. 'cache' => DRUPAL_NO_CACHE, + // Auto-enable in 'content' region by default, which always exists. + // @see system_themes_page(), drupal_render_page() + 'status' => 1, + 'region' => 'content', ); $blocks['powered-by'] = array( 'info' => t('Powered by Drupal'), @@ -2033,6 +2037,9 @@ function system_block_info() { 'info' => t('System help'), 'weight' => '5', 'cache' => DRUPAL_NO_CACHE, + // Auto-enable in 'help' region by default, if the theme defines one. + 'status' => 1, + 'region' => 'help', ); // System-defined menu blocks. foreach (menu_list_system_menus() as $menu_name => $title) { @@ -2553,7 +2560,7 @@ function _system_rebuild_theme_data() { // Now that we've established all our master themes, go back and fill in data // for subthemes. foreach ($sub_themes as $key) { - $themes[$key]->base_themes = system_find_base_themes($themes, $key); + $themes[$key]->base_themes = drupal_find_base_themes($themes, $key); // Don't proceed if there was a problem with the root base theme. if (!current($themes[$key]->base_themes)) { continue; @@ -2648,42 +2655,10 @@ function _system_default_theme_features() { /** * Find all the base themes for the specified theme. * - * Themes can inherit templates and function implementations from earlier themes. - * - * @param $themes - * An array of available themes. - * @param $key - * The name of the theme whose base we are looking for. - * @param $used_keys - * A recursion parameter preventing endless loops. - * @return - * Returns an array of all of the theme's ancestors; the first element's value - * will be NULL if an error occurred. + * This function has been deprecated in favor of drupal_find_base_themes(). */ function system_find_base_themes($themes, $key, $used_keys = array()) { - $base_key = $themes[$key]->info['base theme']; - // Does the base theme exist? - if (!isset($themes[$base_key])) { - return array($base_key => NULL); - } - - $current_base_theme = array($base_key => $themes[$base_key]->info['name']); - - // Is the base theme itself a child of another theme? - if (isset($themes[$base_key]->info['base theme'])) { - // Do we already know the base themes of this theme? - if (isset($themes[$base_key]->base_themes)) { - return $themes[$base_key]->base_themes + $current_base_theme; - } - // Prevent loops. - if (!empty($used_keys[$base_key])) { - return array($base_key => NULL); - } - $used_keys[$base_key] = TRUE; - return system_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme; - } - // If we get here, then this is our parent theme. - return $current_base_theme; + return drupal_find_base_themes($themes, $key, $used_keys); } /** diff --git a/modules/system/theme.api.php b/modules/system/theme.api.php index 7fee81cb6..cd7ecfde8 100644 --- a/modules/system/theme.api.php +++ b/modules/system/theme.api.php @@ -93,11 +93,13 @@ function hook_form_system_theme_settings_alter(&$form, &$form_state) { } /** - * Preprocess theme variables. + * Preprocess theme variables for templates. * * This hook allows modules to preprocess theme variables for theme templates. - * It is called for all invocations of theme(), to allow modules to add to - * or override variables for all theme hooks. + * It is called for all theme hooks implemented as templates, but not for theme + * hooks implemented as functions. hook_preprocess_HOOK() can be used to + * preprocess variables for a specific theme hook, whether implemented as a + * template or function. * * For more detailed information, see theme(). * @@ -159,11 +161,13 @@ function hook_preprocess_HOOK(&$variables) { } /** - * Process theme variables. + * Process theme variables for templates. * - * This hook allows modules to process theme variables for theme templates. - * It is called for all invocations of theme(), to allow modules to add to - * or override variables for all theme hooks. + * This hook allows modules to process theme variables for theme templates. It + * is called for all theme hooks implemented as templates, but not for theme + * hooks implemented as functions. hook_process_HOOK() can be used to process + * variables for a specific theme hook, whether implemented as a template or + * function. * * For more detailed information, see theme(). * @@ -199,7 +203,11 @@ function hook_process(&$variables, $hook) { * The variables array (modify in place). */ function hook_process_HOOK(&$variables) { - $variables['classes'] .= ' my_added_class'; + // @todo There are no use-cases in Drupal core for this hook. Find one from a + // contributed module, or come up with a good example. Coming up with a good + // example might be tough, since the intent is for nearly everything to be + // achievable via preprocess functions, and for process functions to only be + // used when requiring the later execution time. } /** diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc index a236cfed1..828fde0ab 100644 --- a/modules/taxonomy/taxonomy.admin.inc +++ b/modules/taxonomy/taxonomy.admin.inc @@ -696,7 +696,8 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name, ); - field_attach_form('taxonomy_term', $term, $form, $form_state); + $langcode = entity_language('taxonomy_term', $term); + field_attach_form('taxonomy_term', $term, $form, $form_state, $langcode); $form['relations'] = array( '#type' => 'fieldset', diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info index 4d3401adc..17b467703 100644 --- a/modules/taxonomy/taxonomy.info +++ b/modules/taxonomy/taxonomy.info @@ -8,8 +8,8 @@ files[] = taxonomy.module files[] = taxonomy.test configure = admin/structure/taxonomy -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/taxonomy/taxonomy.install b/modules/taxonomy/taxonomy.install index f442c95ef..711a0f983 100644 --- a/modules/taxonomy/taxonomy.install +++ b/modules/taxonomy/taxonomy.install @@ -592,36 +592,105 @@ function taxonomy_update_7005(&$sandbox) { if (!empty($vocabularies)) { $sandbox['vocabularies'] = $vocabularies; } + + db_create_table('taxonomy_update_7005', array( + 'description' => 'Stores temporary data for taxonomy_update_7005.', + 'fields' => array( + 'n' => array( + 'description' => 'Preserve order.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'vocab_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'vid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'created' => array( + 'type' => 'int', + 'not null' => FALSE, + ), + 'sticky' => array( + 'type' => 'int', + 'not null' => FALSE, + ), + 'is_current' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + ), + ), + 'primary key' => array('n'), + )); + + // Query selects all revisions at once and processes them in revision and + // term weight order. + $query = db_select('taxonomy_term_data', 'td'); + // We are migrating term-node relationships. If there are none for a + // term, we do not need the term_data row. + $query->join('taxonomy_term_node', 'tn', 'td.tid = tn.tid'); + // If a term-node relationship exists for a nid that does not exist, we + // cannot migrate it as we have no node to relate it to; thus we do not + // need that row from term_node. + $query->join('node', 'n', 'tn.nid = n.nid'); + // If the current term-node relationship is for the current revision of + // the node, this left join will match and is_current will be non-NULL + // (we also get the current sticky and created in this case). This + // tells us whether to insert into the current data tables in addition + // to the revision data tables. + $query->leftJoin('node', 'n2', 'tn.vid = n2.vid'); + $query->addField('td', 'vid', 'vocab_id'); + $query->addField('td', 'tid'); + $query->addField('tn', 'nid'); + $query->addField('tn', 'vid'); + $query->addField('n', 'type'); + $query->addField('n2', 'created'); + $query->addField('n2', 'sticky'); + $query->addField('n2', 'nid', 'is_current'); + // This query must return a consistent ordering across multiple calls. + // We need them ordered by node vid (since we use that to decide when + // to reset the delta counters) and by term weight so they appear + // within each node in weight order. However, tn.vid,td.weight is not + // guaranteed to be unique, so we add tn.tid as an additional sort key + // because tn.tid,tn.vid is the primary key of the D6 term_node table + // and so is guaranteed unique. Unfortunately it also happens to be in + // the wrong order which is less efficient, but c'est la vie. + $query->orderBy('tn.vid'); + $query->orderBy('td.weight'); + $query->orderBy('tn.tid'); + db_insert('taxonomy_update_7005') + ->from($query) + ->execute(); } else { // We do each pass in batches of 1000. $batch = 1000; - // Query selects all revisions at once and processes them in revision and - // term weight order. Join types: - // - // - INNER JOIN term_node ON tn.tid: We are migrating term-node - // relationships. If there are none for a term, we do not need the - // term_data row. - // - INNER JOIN {node} n ON n.nid: If a term-node relationship exists for a - // nid that does not exist, we cannot migrate it as we have no node to - // relate it to; thus we do not need that row from term_node. - // - LEFT JOIN {node} n2 ON n2.vid: If the current term-node relationship - // is for the current revision of the node, this left join will match and - // is_current will be non-NULL (we also get the current sticky and - // created in this case). This tells us whether to insert into the - // current data tables in addition to the revision data tables. - // - // This query must return a consistent ordering across multiple calls. We - // need them ordered by node vid (since we use that to decide when to reset - // the delta counters) and by term weight so they appear within each node - // in weight order. However, tn.vid,td.weight is not guaranteed to be - // unique, so we add tn.tid as an additional sort key because tn.tid,tn.vid - // is the primary key of the D6 term_node table and so is guaranteed - // unique. Unfortunately it also happens to be in the wrong order which is - // less efficient, but c'est la vie. - $query = 'SELECT td.vid AS vocab_id, td.tid, tn.nid, tn.vid, n.type, n2.created, n2.sticky, n2.nid AS is_current FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n ON tn.nid = n.nid LEFT JOIN {node} n2 ON tn.vid = n2.vid ORDER BY tn.vid, td.weight ASC, tn.tid'; - $result = db_query_range($query, $sandbox['last'], $batch); + $result = db_query_range('SELECT vocab_id, tid, nid, vid, type, created, sticky, is_current FROM {taxonomy_update_7005} ORDER BY n', $sandbox['last'], $batch); if (isset($sandbox['cursor'])) { $values = $sandbox['cursor']['values']; $deltas = $sandbox['cursor']['deltas']; @@ -714,6 +783,7 @@ function taxonomy_update_7005(&$sandbox) { db_drop_table('taxonomy_term_node'); // If there are no vocabs, we're done. + db_drop_table('taxonomy_update_7005'); $sandbox['#finished'] = TRUE; // Determine necessity of taxonomyextras field. diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index d7e9b2f1c..d501282fe 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -1136,7 +1136,7 @@ class TaxonomyTermController extends DrupalDefaultEntityController { if (isset($conditions['name'])) { $query_conditions = &$query->conditions(); foreach ($query_conditions as $key => $condition) { - if ($condition['field'] == 'base.name') { + if (is_array($condition) && $condition['field'] == 'base.name') { $query_conditions[$key]['operator'] = 'LIKE'; $query_conditions[$key]['value'] = db_like($query_conditions[$key]['value']); } @@ -1270,7 +1270,8 @@ function taxonomy_vocabulary_machine_name_load($name) { * A term's ID * * @return - * A term object. Results are statically cached. + * A taxonomy term object, or FALSE if the term was not found. Results are + * statically cached. */ function taxonomy_term_load($tid) { if (!is_numeric($tid)) { @@ -1904,5 +1905,37 @@ function taxonomy_taxonomy_term_delete($term) { } /** - * @} End of "defgroup taxonomy_index" + * @} End of "defgroup taxonomy_index". */ + +/** + * Implements hook_entity_query_alter(). + * + * Converts EntityFieldQuery instances on taxonomy terms that have an entity + * condition on term bundles (vocabulary machine names). Since the vocabulary + * machine name is not present in the {taxonomy_term_data} table itself, we have + * to convert the bundle condition into a proprety condition of vocabulary IDs + * to match against {taxonomy_term_data}.vid. + */ +function taxonomy_entity_query_alter($query) { + $conditions = &$query->entityConditions; + + // Alter only taxonomy term queries with bundle conditions. + if (isset($conditions['entity_type']) && $conditions['entity_type']['value'] == 'taxonomy_term' && isset($conditions['bundle'])) { + // Convert vocabulary machine names to vocabulary IDs. + $vocabulary_data = taxonomy_vocabulary_get_names(); + $vids = array(); + if (is_array($conditions['bundle']['value'])) { + foreach ($conditions['bundle']['value'] as $vocabulary_machine_name) { + $vids[] = $vocabulary_data[$vocabulary_machine_name]->vid; + } + } + else { + $vocabulary_machine_name = $conditions['bundle']['value']; + $vids = $vocabulary_data[$vocabulary_machine_name]->vid; + } + + $query->propertyCondition('vid', $vids, $conditions['bundle']['operator']); + unset($conditions['bundle']); + } +} diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test index 42b4d4767..32ae84d66 100644 --- a/modules/taxonomy/taxonomy.test +++ b/modules/taxonomy/taxonomy.test @@ -6,8 +6,8 @@ */ /** -* Class with common helper methods. -*/ + * Provides common helper methods for Taxonomy module tests. + */ class TaxonomyWebTestCase extends DrupalWebTestCase { /** @@ -39,11 +39,12 @@ class TaxonomyWebTestCase extends DrupalWebTestCase { taxonomy_term_save($term); return $term; } + } /** -* Tests for the taxonomy vocabulary interface. -*/ + * Tests the taxonomy vocabulary interface. + */ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase { public static function getInfo() { @@ -76,17 +77,17 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase { $edit['description'] = $this->randomName(); $edit['machine_name'] = $machine_name; $this->drupalPost(NULL, $edit, t('Save')); - $this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), t('Vocabulary created successfully')); + $this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), 'Vocabulary created successfully.'); // Edit the vocabulary. $this->drupalGet('admin/structure/taxonomy'); - $this->assertText($edit['name'], t('Vocabulary found in the vocabulary overview listing.')); + $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.'); $this->clickLink(t('edit vocabulary')); $edit = array(); $edit['name'] = $this->randomName(); $this->drupalPost(NULL, $edit, t('Save')); $this->drupalGet('admin/structure/taxonomy'); - $this->assertText($edit['name'], t('Vocabulary found in the vocabulary overview listing.')); + $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.'); // Try to submit a vocabulary with a duplicate machine name. $edit['machine_name'] = $machine_name; @@ -123,7 +124,7 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase { // Check that the weights are saved in the database correctly. foreach ($vocabularies as $key => $vocabulary) { - $this->assertEqual($new_vocabularies[$key]->weight, $vocabularies[$key]->weight, t('The vocabulary weight was changed.')); + $this->assertEqual($new_vocabularies[$key]->weight, $vocabularies[$key]->weight, 'The vocabulary weight was changed.'); } } @@ -137,10 +138,10 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase { taxonomy_vocabulary_delete($key); } // Confirm that no vocabularies are found in the database. - $this->assertFalse(taxonomy_get_vocabularies(), t('No vocabularies found in the database')); + $this->assertFalse(taxonomy_get_vocabularies(), 'No vocabularies found in the database.'); $this->drupalGet('admin/structure/taxonomy'); // Check the default message for no vocabularies. - $this->assertText(t('No vocabularies available.'), t('No vocabularies were found.')); + $this->assertText(t('No vocabularies available.'), 'No vocabularies were found.'); } /** @@ -153,29 +154,29 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase { 'machine_name' => drupal_strtolower($this->randomName()), ); $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save')); - $this->assertText(t('Created new vocabulary'), t('New vocabulary was created.')); + $this->assertText(t('Created new vocabulary'), 'New vocabulary was created.'); // Check the created vocabulary. $vocabularies = taxonomy_get_vocabularies(); - $vid = $vocabularies[count($vocabularies)-1]->vid; + $vid = $vocabularies[count($vocabularies) - 1]->vid; entity_get_controller('taxonomy_vocabulary')->resetCache(); $vocabulary = taxonomy_vocabulary_load($vid); - $this->assertTrue($vocabulary, t('Vocabulary found in database')); + $this->assertTrue($vocabulary, 'Vocabulary found in database.'); // Delete the vocabulary. $edit = array(); $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/edit', $edit, t('Delete')); - $this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->name)), t('[confirm deletion] Asks for confirmation.')); - $this->assertText(t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), t('[confirm deletion] Inform that all terms will be deleted.')); + $this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->name)), '[confirm deletion] Asks for confirmation.'); + $this->assertText(t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), '[confirm deletion] Inform that all terms will be deleted.'); // Confirm deletion. $this->drupalPost(NULL, NULL, t('Delete')); - $this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), t('Vocabulary deleted')); + $this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), 'Vocabulary deleted.'); entity_get_controller('taxonomy_vocabulary')->resetCache(); - $this->assertFalse(taxonomy_vocabulary_load($vid), t('Vocabulary is not found in the database')); + $this->assertFalse(taxonomy_vocabulary_load($vid), 'Vocabulary is not found in the database.'); } -} +} /** * Tests for taxonomy vocabulary functions. @@ -207,14 +208,14 @@ class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase { $vid = count($vocabularies) + 1; $vocabulary = taxonomy_vocabulary_load($vid); // This should not return an object because no such vocabulary exists. - $this->assertTrue(empty($vocabulary), t('No object loaded.')); + $this->assertTrue(empty($vocabulary), 'No object loaded.'); // Create a new vocabulary. $this->createVocabulary(); // Load the vocabulary with the same $vid from earlier. // This should return a vocabulary object since it now matches a real vid. $vocabulary = taxonomy_vocabulary_load($vid); - $this->assertTrue(!empty($vocabulary) && is_object($vocabulary), t('Vocabulary is an object')); + $this->assertTrue(!empty($vocabulary) && is_object($vocabulary), 'Vocabulary is an object.'); $this->assertEqual($vocabulary->vid, $vid, 'Valid vocabulary vid is the same as our previously invalid one.'); } @@ -257,8 +258,8 @@ class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase { */ function testTaxonomyVocabularyLoadStaticReset() { $original_vocabulary = taxonomy_vocabulary_load($this->vocabulary->vid); - $this->assertTrue(is_object($original_vocabulary), t('Vocabulary loaded successfully')); - $this->assertEqual($this->vocabulary->name, $original_vocabulary->name, t('Vocabulary loaded successfully')); + $this->assertTrue(is_object($original_vocabulary), 'Vocabulary loaded successfully.'); + $this->assertEqual($this->vocabulary->name, $original_vocabulary->name, 'Vocabulary loaded successfully.'); // Change the name and description. $vocabulary = $original_vocabulary; @@ -274,7 +275,7 @@ class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase { // Delete the vocabulary. taxonomy_vocabulary_delete($this->vocabulary->vid); $vocabularies = taxonomy_get_vocabularies(); - $this->assertTrue(!isset($vocabularies[$this->vocabulary->vid]), t('The vocabulary was deleted')); + $this->assertTrue(!isset($vocabularies[$this->vocabulary->vid]), 'The vocabulary was deleted.'); } /** @@ -301,21 +302,21 @@ class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase { // Fetch the names for all vocabularies, confirm that they are keyed by // machine name. $names = taxonomy_vocabulary_get_names(); - $this->assertEqual($names[$vocabulary1->machine_name]->name, $vocabulary1->name, t('Vocabulary 1 name found.')); + $this->assertEqual($names[$vocabulary1->machine_name]->name, $vocabulary1->name, 'Vocabulary 1 name found.'); // Fetch all of the vocabularies using taxonomy_get_vocabularies(). // Confirm that the vocabularies are ordered by weight. $vocabularies = taxonomy_get_vocabularies(); - $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary1->vid, t('Vocabulary was found in the vocabularies array.')); - $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary2->vid, t('Vocabulary was found in the vocabularies array.')); - $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary3->vid, t('Vocabulary was found in the vocabularies array.')); + $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary1->vid, 'Vocabulary was found in the vocabularies array.'); + $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary2->vid, 'Vocabulary was found in the vocabularies array.'); + $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary3->vid, 'Vocabulary was found in the vocabularies array.'); // Fetch the vocabularies with taxonomy_vocabulary_load_multiple(), specifying IDs. // Ensure they are returned in the same order as the original array. $vocabularies = taxonomy_vocabulary_load_multiple(array($vocabulary3->vid, $vocabulary2->vid, $vocabulary1->vid)); - $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary3->vid, t('Vocabulary loaded successfully by ID.')); - $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary2->vid, t('Vocabulary loaded successfully by ID.')); - $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary1->vid, t('Vocabulary loaded successfully by ID.')); + $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary3->vid, 'Vocabulary loaded successfully by ID.'); + $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary2->vid, 'Vocabulary loaded successfully by ID.'); + $this->assertEqual(array_shift($vocabularies)->vid, $vocabulary1->vid, 'Vocabulary loaded successfully by ID.'); // Fetch vocabulary 1 by name. $vocabulary = current(taxonomy_vocabulary_load_multiple(array(), array('name' => $vocabulary1->name))); @@ -348,7 +349,7 @@ class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase { taxonomy_vocabulary_save($this->vocabulary); // Check that the field instance is still attached to the vocabulary. - $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), t('The bundle name was updated correctly.')); + $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), 'The bundle name was updated correctly.'); } /** @@ -383,6 +384,7 @@ class TaxonomyVocabularyTestCase extends TaxonomyWebTestCase { field_create_field($this->field); field_create_instance($this->instance); } + } /** @@ -404,7 +406,7 @@ class TaxonomyTermFunctionTestCase extends TaxonomyWebTestCase { // Delete a valid term. taxonomy_term_delete($valid_term->tid); $terms = taxonomy_term_load_multiple(array(), array('vid' => $vocabulary->vid)); - $this->assertTrue(empty($terms), 'Vocabulary is empty after deletion'); + $this->assertTrue(empty($terms), 'Vocabulary is empty after deletion.'); // Delete an invalid term. Should not throw any notices. taxonomy_term_delete(42); @@ -442,7 +444,6 @@ class TaxonomyTermFunctionTestCase extends TaxonomyWebTestCase { * ---- term[2] | depth: 2 * ------ term[3] | depth: 3 */ - // Count $term[1] parents with $max_depth = 1. $tree = taxonomy_get_tree($vocabulary->vid, $term[1]->tid, 1); $this->assertEqual(1, count($tree), 'We have one parent with depth 1.'); @@ -452,7 +453,7 @@ class TaxonomyTermFunctionTestCase extends TaxonomyWebTestCase { $this->assertEqual(8, count($tree), 'We have all vocabulary tree elements.'); // Count elements in every tree depth. - foreach($tree as $element) { + foreach ($tree as $element) { if (!isset($depth_count[$element->depth])) { $depth_count[$element->depth] = 0; } @@ -462,7 +463,8 @@ class TaxonomyTermFunctionTestCase extends TaxonomyWebTestCase { $this->assertEqual(2, $depth_count[1], 'Two elements in taxonomy tree depth 1.'); $this->assertEqual(2, $depth_count[2], 'Two elements in taxonomy tree depth 2.'); $this->assertEqual(1, $depth_count[3], 'One element in taxonomy tree depth 3.'); - } + } + } /** @@ -498,8 +500,9 @@ class TaxonomyLegacyTestCase extends TaxonomyWebTestCase { $this->drupalPost('node/add/article', $edit, t('Save')); // Checks that the node has been saved. $node = $this->drupalGetNodeByTitle($edit['title']); - $this->assertEqual($node->created, strtotime($edit['date']), t('Legacy node was saved with the right date.')); + $this->assertEqual($node->created, strtotime($edit['date']), 'Legacy node was saved with the right date.'); } + } /** @@ -572,21 +575,21 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Check the hierarchy. $children = taxonomy_get_children($term1->tid); $parents = taxonomy_get_parents($term2->tid); - $this->assertTrue(isset($children[$term2->tid]), t('Child found correctly.')); - $this->assertTrue(isset($parents[$term1->tid]), t('Parent found correctly.')); + $this->assertTrue(isset($children[$term2->tid]), 'Child found correctly.'); + $this->assertTrue(isset($parents[$term1->tid]), 'Parent found correctly.'); // Load and save a term, confirming that parents are still set. $term = taxonomy_term_load($term2->tid); taxonomy_term_save($term); $parents = taxonomy_get_parents($term2->tid); - $this->assertTrue(isset($parents[$term1->tid]), t('Parent found correctly.')); + $this->assertTrue(isset($parents[$term1->tid]), 'Parent found correctly.'); // Create a third term and save this as a parent of term2. $term3 = $this->createTerm($this->vocabulary); $term2->parent = array($term1->tid, $term3->tid); taxonomy_term_save($term2); $parents = taxonomy_get_parents($term2->tid); - $this->assertTrue(isset($parents[$term1->tid]) && isset($parents[$term3->tid]), t('Both parents found successfully.')); + $this->assertTrue(isset($parents[$term1->tid]) && isset($parents[$term3->tid]), 'Both parents found successfully.'); } /** @@ -604,26 +607,26 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $langcode = LANGUAGE_NONE; $edit["title"] = $this->randomName(); $edit["body[$langcode][0][value]"] = $this->randomName(); - $edit[$this->instance['field_name'] . '[' . $langcode .'][]'] = $term1->tid; + $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $term1->tid; $this->drupalPost('node/add/article', $edit, t('Save')); // Check that the term is displayed when the node is viewed. $node = $this->drupalGetNodeByTitle($edit["title"]); $this->drupalGet('node/' . $node->nid); - $this->assertText($term1->name, t('Term is displayed when viewing the node.')); + $this->assertText($term1->name, 'Term is displayed when viewing the node.'); // Edit the node with a different term. $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $term2->tid; $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); $this->drupalGet('node/' . $node->nid); - $this->assertText($term2->name, t('Term is displayed when viewing the node.')); + $this->assertText($term2->name, 'Term is displayed when viewing the node.'); // Preview the node. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Preview')); - $this->assertNoUniqueText($term2->name, t('Term is displayed when previewing the node.')); + $this->assertNoUniqueText($term2->name, 'Term is displayed when previewing the node.'); $this->drupalPost(NULL, NULL, t('Preview')); - $this->assertNoUniqueText($term2->name, t('Term is displayed when previewing the node again.')); + $this->assertNoUniqueText($term2->name, 'Term is displayed when previewing the node again.'); } /** @@ -652,19 +655,19 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Preview and verify the terms appear but are not created. $this->drupalPost('node/add/page', $edit, t('Preview')); foreach ($terms as $term) { - $this->assertText($term, t('The term appears on the node preview')); + $this->assertText($term, 'The term appears on the node preview.'); } $tree = taxonomy_get_tree($this->vocabulary->vid); - $this->assertTrue(empty($tree), t('The terms are not created on preview.')); + $this->assertTrue(empty($tree), 'The terms are not created on preview.'); // taxonomy.module does not maintain its static caches. drupal_static_reset(); // Save, creating the terms. $this->drupalPost('node/add/page', $edit, t('Save')); - $this->assertRaw(t('@type %title has been created.', array('@type' => t('Basic page'), '%title' => $edit["title"])), t('The node was created successfully')); + $this->assertRaw(t('@type %title has been created.', array('@type' => t('Basic page'), '%title' => $edit["title"])), 'The node was created successfully.'); foreach ($terms as $term) { - $this->assertText($term, t('The term was saved and appears on the node page')); + $this->assertText($term, 'The term was saved and appears on the node page.'); } // Get the created terms. @@ -684,29 +687,29 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $this->drupalGet('node/' . $node->nid); foreach ($term_names as $term_name) { - $this->assertText($term_name, t('The term %name appears on the node page after one term %deleted was deleted', array('%name' => $term_name, '%deleted' => $term_objects['term1']->name))); + $this->assertText($term_name, format_string('The term %name appears on the node page after one term %deleted was deleted', array('%name' => $term_name, '%deleted' => $term_objects['term1']->name))); } - $this->assertNoText($term_objects['term1']->name, t('The deleted term %name does not appear on the node page.', array('%name' => $term_objects['term1']->name))); + $this->assertNoText($term_objects['term1']->name, format_string('The deleted term %name does not appear on the node page.', array('%name' => $term_objects['term1']->name))); // Test autocomplete on term 2, which contains a comma. // The term will be quoted, and the " will be encoded in unicode (\u0022). $input = substr($term_objects['term2']->name, 0, 3); $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input); - $this->assertRaw('{"\u0022' . $term_objects['term2']->name . '\u0022":"' . $term_objects['term2']->name . '"}', t('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term2']->name))); + $this->assertRaw('{"\u0022' . $term_objects['term2']->name . '\u0022":"' . $term_objects['term2']->name . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term2']->name))); // Test autocomplete on term 3 - it is alphanumeric only, so no extra // quoting. $input = substr($term_objects['term3']->name, 0, 3); $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input); - $this->assertRaw('{"' . $term_objects['term3']->name . '":"' . $term_objects['term3']->name . '"}', t('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->name))); + $this->assertRaw('{"' . $term_objects['term3']->name . '":"' . $term_objects['term3']->name . '"}', format_string('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->name))); // Test taxonomy autocomplete with a nonexistent field. $field_name = $this->randomName(); $tag = $this->randomName(); $message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name)); - $this->assertFalse(field_info_field($field_name), t('Field %field_name does not exist.', array('%field_name' => $field_name))); + $this->assertFalse(field_info_field($field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name))); $this->drupalGet('taxonomy/autocomplete/' . $field_name . '/' . $tag); - $this->assertRaw($message, t('Autocomplete returns correct error message when the taxonomy field does not exist.')); + $this->assertRaw($message, 'Autocomplete returns correct error message when the taxonomy field does not exist.'); } /** @@ -735,8 +738,8 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $url = url($path, array('absolute' => TRUE)); $result = drupal_http_request($url); $data = drupal_json_decode($result->data); - $this->assertEqual($data[$first_term->name], check_plain($first_term->name), 'Autocomplete returned the first matching term'); - $this->assertEqual($data[$second_term->name], check_plain($second_term->name), 'Autocomplete returned the second matching term'); + $this->assertEqual($data[$first_term->name], check_plain($first_term->name), 'Autocomplete returned the first matching term.'); + $this->assertEqual($data[$second_term->name], check_plain($second_term->name), 'Autocomplete returned the second matching term.'); // Try to autocomplete a term name that matches first term. // We should only get the first term in a json encoded string. @@ -778,7 +781,7 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $terms = taxonomy_get_term_by_name($edit['name']); $term = reset($terms); - $this->assertNotNull($term, t('Term found in database')); + $this->assertNotNull($term, 'Term found in database.'); // Submitting a term takes us to the add page; we need the List page. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name); @@ -788,8 +791,8 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // the first edit link found on the listing page is to our term. $this->clickLink(t('edit')); - $this->assertRaw($edit['name'], t('The randomly generated term name is present.')); - $this->assertText($edit['description[value]'], t('The randomly generated term description is present.')); + $this->assertRaw($edit['name'], 'The randomly generated term name is present.'); + $this->assertText($edit['description[value]'], 'The randomly generated term description is present.'); $edit = array( 'name' => $this->randomName(14), @@ -801,13 +804,13 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Check that the term is still present at admin UI after edit. $this->drupalGet('admin/structure/taxonomy/' . $this->vocabulary->machine_name); - $this->assertText($edit['name'], t('The randomly generated term name is present.')); + $this->assertText($edit['name'], 'The randomly generated term name is present.'); $this->assertLink(t('edit')); // View the term and check that it is correct. $this->drupalGet('taxonomy/term/' . $term->tid); - $this->assertText($edit['name'], t('The randomly generated term name is present.')); - $this->assertText($edit['description[value]'], t('The randomly generated term description is present.')); + $this->assertText($edit['name'], 'The randomly generated term name is present.'); + $this->assertText($edit['description[value]'], 'The randomly generated term description is present.'); // Did this page request display a 'term-listing-heading'? $this->assertPattern('|class="taxonomy-term-description"|', 'Term page displayed the term description element.'); @@ -830,7 +833,7 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Assert that the term no longer exists. $this->drupalGet('taxonomy/term/' . $term->tid); - $this->assertResponse(404, t('The taxonomy term page was not found')); + $this->assertResponse(404, 'The taxonomy term page was not found.'); } /** @@ -874,9 +877,9 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { drupal_static_reset('taxonomy_get_treeparent'); drupal_static_reset('taxonomy_get_treeterms'); $terms = taxonomy_get_tree($this->vocabulary->vid); - $this->assertEqual($terms[0]->tid, $term2->tid, t('Term 2 was moved above term 1.')); - $this->assertEqual($terms[1]->parents, array($term2->tid), t('Term 3 was made a child of term 2.')); - $this->assertEqual($terms[2]->tid, $term1->tid, t('Term 1 was moved below term 2.')); + $this->assertEqual($terms[0]->tid, $term2->tid, 'Term 2 was moved above term 1.'); + $this->assertEqual($terms[1]->parents, array($term2->tid), 'Term 3 was made a child of term 2.'); + $this->assertEqual($terms[2]->tid, $term1->tid, 'Term 1 was moved below term 2.'); $this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name, array(), t('Reset to alphabetical')); // Submit confirmation form. @@ -886,10 +889,10 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { drupal_static_reset('taxonomy_get_treeparent'); drupal_static_reset('taxonomy_get_treeterms'); $terms = taxonomy_get_tree($this->vocabulary->vid); - $this->assertEqual($terms[0]->tid, $term1->tid, t('Term 1 was moved to back above term 2.')); - $this->assertEqual($terms[1]->tid, $term2->tid, t('Term 2 was moved to back below term 1.')); - $this->assertEqual($terms[2]->tid, $term3->tid, t('Term 3 is still below term 2.')); - $this->assertEqual($terms[2]->parents, array($term2->tid), t('Term 3 is still a child of term 2.').var_export($terms[1]->tid,1)); + $this->assertEqual($terms[0]->tid, $term1->tid, 'Term 1 was moved to back above term 2.'); + $this->assertEqual($terms[1]->tid, $term2->tid, 'Term 2 was moved to back below term 1.'); + $this->assertEqual($terms[2]->tid, $term3->tid, 'Term 3 is still below term 2.'); + $this->assertEqual($terms[2]->parents, array($term2->tid), 'Term 3 is still a child of term 2.' . var_export($terms[1]->tid, 1)); } /** @@ -911,14 +914,14 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Check that the term was successfully created. $terms = taxonomy_get_term_by_name($edit['name']); $term = reset($terms); - $this->assertNotNull($term, t('Term found in database')); - $this->assertEqual($edit['name'], $term->name, t('Term name was successfully saved.')); - $this->assertEqual($edit['description[value]'], $term->description, t('Term description was successfully saved.')); + $this->assertNotNull($term, 'Term found in database.'); + $this->assertEqual($edit['name'], $term->name, 'Term name was successfully saved.'); + $this->assertEqual($edit['description[value]'], $term->description, 'Term description was successfully saved.'); // Check that the parent tid is still there. The other parent (<root>) is // not added by taxonomy_get_parents(). $parents = taxonomy_get_parents($term->tid); $parent = reset($parents); - $this->assertEqual($edit['parent[]'][1], $parent->tid, t('Term parents were successfully saved.')); + $this->assertEqual($edit['parent[]'][1], $parent->tid, 'Term parents were successfully saved.'); } /** @@ -929,19 +932,19 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Load the term with the exact name. $terms = taxonomy_get_term_by_name($term->name); - $this->assertTrue(isset($terms[$term->tid]), t('Term loaded using exact name.')); + $this->assertTrue(isset($terms[$term->tid]), 'Term loaded using exact name.'); // Load the term with space concatenated. $terms = taxonomy_get_term_by_name(' ' . $term->name . ' '); - $this->assertTrue(isset($terms[$term->tid]), t('Term loaded with extra whitespace.')); + $this->assertTrue(isset($terms[$term->tid]), 'Term loaded with extra whitespace.'); // Load the term with name uppercased. $terms = taxonomy_get_term_by_name(strtoupper($term->name)); - $this->assertTrue(isset($terms[$term->tid]), t('Term loaded with uppercased name.')); + $this->assertTrue(isset($terms[$term->tid]), 'Term loaded with uppercased name.'); // Load the term with name lowercased. $terms = taxonomy_get_term_by_name(strtolower($term->name)); - $this->assertTrue(isset($terms[$term->tid]), t('Term loaded with lowercased name.')); + $this->assertTrue(isset($terms[$term->tid]), 'Term loaded with lowercased name.'); // Try to load an invalid term name. $terms = taxonomy_get_term_by_name('Banana'); @@ -960,12 +963,12 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Load multiple terms with the same name. $terms = taxonomy_get_term_by_name($term->name); - $this->assertEqual(count($terms), 2, t('Two terms loaded with the same name.')); + $this->assertEqual(count($terms), 2, 'Two terms loaded with the same name.'); // Load single term when restricted to one vocabulary. $terms = taxonomy_get_term_by_name($term->name, $this->vocabulary->machine_name); - $this->assertEqual(count($terms), 1, t('One term loaded when restricted by vocabulary.')); - $this->assertTrue(isset($terms[$term->tid]), t('Term loaded using exact name and vocabulary machine name.')); + $this->assertEqual(count($terms), 1, 'One term loaded when restricted by vocabulary.'); + $this->assertTrue(isset($terms[$term->tid]), 'Term loaded using exact name and vocabulary machine name.'); // Create a new term with another name. $term2 = $this->createTerm($this->vocabulary); @@ -973,12 +976,13 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Try to load a term by name that doesn't exist in this vocabulary but // exists in another vocabulary. $terms = taxonomy_get_term_by_name($term2->name, $new_vocabulary->machine_name); - $this->assertFalse($terms, t('Invalid term name restricted by vocabulary machine name not loaded.')); + $this->assertFalse($terms, 'Invalid term name restricted by vocabulary machine name not loaded.'); // Try to load terms filtering by a non-existing vocabulary. $terms = taxonomy_get_term_by_name($term2->name, 'non_existing_vocabulary'); - $this->assertEqual(count($terms), 0, t('No terms loaded when restricted by a non-existing vocabulary.')); + $this->assertEqual(count($terms), 0, 'No terms loaded when restricted by a non-existing vocabulary.'); } + } /** @@ -1058,7 +1062,7 @@ class TaxonomyRSSTestCase extends TaxonomyWebTestCase { $edit = array(); $langcode = LANGUAGE_NONE; $edit["title"] = $this->randomName(); - $edit[$this->instance['field_name'] . '[' . $langcode .'][]'] = $term1->tid; + $edit[$this->instance['field_name'] . '[' . $langcode . '][]'] = $term1->tid; $this->drupalPost('node/add/article', $edit, t('Save')); // Check that the term is displayed when the RSS feed is viewed. @@ -1072,6 +1076,7 @@ class TaxonomyRSSTestCase extends TaxonomyWebTestCase { ); $this->assertRaw(format_xml_elements(array($test_element)), 'Term is displayed when viewing the rss feed.'); } + } /** @@ -1181,7 +1186,7 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { ':nid' => $node->nid, ':tid' => $term_1->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 1 is indexed once.')); + $this->assertEqual(1, $index_count, 'Term 1 is indexed once.'); // Update the article to change one term. $edit["{$this->field_name_1}[$langcode][]"] = $term_2->tid; @@ -1192,12 +1197,12 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { ':nid' => $node->nid, ':tid' => $term_1->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 1 is indexed.')); + $this->assertEqual(1, $index_count, 'Term 1 is indexed.'); $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( ':nid' => $node->nid, ':tid' => $term_2->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 2 is indexed.')); + $this->assertEqual(1, $index_count, 'Term 2 is indexed.'); // Update the article to change another term. $edit["{$this->field_name_2}[$langcode][]"] = $term_2->tid; @@ -1208,12 +1213,12 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { ':nid' => $node->nid, ':tid' => $term_1->tid, ))->fetchField(); - $this->assertEqual(0, $index_count, t('Term 1 is not indexed.')); + $this->assertEqual(0, $index_count, 'Term 1 is not indexed.'); $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( ':nid' => $node->nid, ':tid' => $term_2->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 2 is indexed once.')); + $this->assertEqual(1, $index_count, 'Term 2 is indexed once.'); // Redo the above tests without interface. $update_node = array( @@ -1233,12 +1238,12 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { ':nid' => $node->nid, ':tid' => $term_1->tid, ))->fetchField(); - $this->assertEqual(0, $index_count, t('Term 1 is not indexed.')); + $this->assertEqual(0, $index_count, 'Term 1 is not indexed.'); $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( ':nid' => $node->nid, ':tid' => $term_2->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 2 is indexed once.')); + $this->assertEqual(1, $index_count, 'Term 2 is indexed once.'); // Update the article to change one term. $update_node[$this->field_name_1][$langcode] = array(array('tid' => $term_1->tid)); @@ -1250,12 +1255,12 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { ':nid' => $node->nid, ':tid' => $term_1->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 1 is indexed.')); + $this->assertEqual(1, $index_count, 'Term 1 is indexed.'); $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( ':nid' => $node->nid, ':tid' => $term_2->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 2 is indexed.')); + $this->assertEqual(1, $index_count, 'Term 2 is indexed.'); // Update the article to change another term. $update_node[$this->field_name_2][$langcode] = array(array('tid' => $term_1->tid)); @@ -1267,12 +1272,12 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { ':nid' => $node->nid, ':tid' => $term_1->tid, ))->fetchField(); - $this->assertEqual(1, $index_count, t('Term 1 is indexed once.')); + $this->assertEqual(1, $index_count, 'Term 1 is indexed once.'); $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( ':nid' => $node->nid, ':tid' => $term_2->tid, ))->fetchField(); - $this->assertEqual(0, $index_count, t('Term 2 is not indexed.')); + $this->assertEqual(0, $index_count, 'Term 2 is not indexed.'); } /** @@ -1287,8 +1292,9 @@ class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { // Verify that the page breadcrumbs include a link to the parent term. $this->drupalGet('taxonomy/term/' . $term1->tid); - $this->assertRaw(l($term2->name, 'taxonomy/term/' . $term2->tid), t('Parent term link is displayed when viewing the node.')); + $this->assertRaw(l($term2->name, 'taxonomy/term/' . $term2->tid), 'Parent term link is displayed when viewing the node.'); } + } /** @@ -1331,8 +1337,8 @@ class TaxonomyLoadMultipleTestCase extends TaxonomyWebTestCase { // Load the same terms again by tid. $terms2 = taxonomy_term_load_multiple(array_keys($terms)); - $this->assertEqual($count, count($terms2), 'Five terms were loaded by tid'); - $this->assertEqual($terms, $terms2, t('Both arrays contain the same terms')); + $this->assertEqual($count, count($terms2), 'Five terms were loaded by tid.'); + $this->assertEqual($terms, $terms2, 'Both arrays contain the same terms.'); // Load the terms by tid, with a condition on vid. $terms3 = taxonomy_term_load_multiple(array_keys($terms2), array('vid' => $vocabulary->vid)); @@ -1352,9 +1358,9 @@ class TaxonomyLoadMultipleTestCase extends TaxonomyWebTestCase { // Create a single term and load it by name. $term = $this->createTerm($vocabulary); $loaded_terms = taxonomy_term_load_multiple(array(), array('name' => $term->name)); - $this->assertEqual(count($loaded_terms), 1, t('One term was loaded')); + $this->assertEqual(count($loaded_terms), 1, 'One term was loaded.'); $loaded_term = reset($loaded_terms); - $this->assertEqual($term->tid, $loaded_term->tid, t('Term loaded by name successfully.')); + $this->assertEqual($term->tid, $loaded_term->tid, 'Term loaded by name successfully.'); } } @@ -1390,7 +1396,7 @@ class TaxonomyHooksTestCase extends TaxonomyWebTestCase { $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save')); $terms = taxonomy_get_term_by_name($edit['name']); $term = reset($terms); - $this->assertEqual($term->antonym, $edit['antonym'], t('Antonym was loaded into the term object')); + $this->assertEqual($term->antonym, $edit['antonym'], 'Antonym was loaded into the term object.'); // Update the term with a different antonym. $edit = array( @@ -1400,26 +1406,28 @@ class TaxonomyHooksTestCase extends TaxonomyWebTestCase { $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', $edit, t('Save')); taxonomy_terms_static_reset(); $term = taxonomy_term_load($term->tid); - $this->assertEqual($edit['antonym'], $term->antonym, t('Antonym was successfully edited')); + $this->assertEqual($edit['antonym'], $term->antonym, 'Antonym was successfully edited.'); // Delete the term. taxonomy_term_delete($term->tid); $antonym = db_query('SELECT tid FROM {taxonomy_term_antonym} WHERE tid = :tid', array(':tid' => $term->tid))->fetchField(); - $this->assertFalse($antonym, t('The antonym were deleted from the database.')); + $this->assertFalse($antonym, 'The antonym were deleted from the database.'); } + } /** * Tests for taxonomy term field and formatter. */ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { + protected $instance; protected $vocabulary; public static function getInfo() { return array( - 'name' => 'Taxonomy term reference field', - 'description' => 'Test the creation of term fields.', + 'name' => 'Taxonomy term reference field', + 'description' => 'Test the creation of term fields.', 'group' => 'Taxonomy', ); } @@ -1473,10 +1481,10 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $entity->{$this->field_name}[$langcode][0]['tid'] = $term->tid; try { field_attach_validate('test_entity', $entity); - $this->pass(t('Correct term does not cause validation error')); + $this->pass('Correct term does not cause validation error.'); } catch (FieldValidationException $e) { - $this->fail(t('Correct term does not cause validation error')); + $this->fail('Correct term does not cause validation error.'); } $entity = field_test_create_stub_entity(); @@ -1484,10 +1492,10 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $entity->{$this->field_name}[$langcode][0]['tid'] = $bad_term->tid; try { field_attach_validate('test_entity', $entity); - $this->fail(t('Wrong term causes validation error')); + $this->fail('Wrong term causes validation error.'); } catch (FieldValidationException $e) { - $this->pass(t('Wrong term causes validation error')); + $this->pass('Wrong term causes validation error.'); } } @@ -1501,7 +1509,7 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { // Display creation form. $langcode = LANGUAGE_NONE; $this->drupalGet('test-entity/add/test-bundle'); - $this->assertFieldByName("{$this->field_name}[$langcode]", '', t('Widget is displayed')); + $this->assertFieldByName("{$this->field_name}[$langcode]", '', 'Widget is displayed.'); // Submit with some value. $edit = array( @@ -1510,7 +1518,7 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $this->drupalPost(NULL, $edit, t('Save')); preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match); $id = $match[1]; - $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), t('Entity was created')); + $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created.'); // Display the object. $entity = field_test_entity_test_load($id); @@ -1518,12 +1526,12 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { field_attach_prepare_view('test_entity', $entities, 'full'); $entity->content = field_attach_view('test_entity', $entity, 'full'); $this->content = drupal_render($entity->content); - $this->assertText($term->name, t('Term name is displayed')); + $this->assertText($term->name, 'Term name is displayed.'); // Delete the vocabulary and verify that the widget is gone. taxonomy_vocabulary_delete($this->vocabulary->vid); $this->drupalGet('test-entity/add/test-bundle'); - $this->assertNoFieldByName("{$this->field_name}[$langcode]", '', 'Widget is not displayed'); + $this->assertNoFieldByName("{$this->field_name}[$langcode]", '', 'Widget is not displayed.'); } /** @@ -1555,30 +1563,32 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { // Check that entity bundles are properly updated. $info = entity_get_info('taxonomy_term'); - $this->assertFalse(isset($info['bundles'][$old_name]), t('The old bundle name does not appear in entity_get_info().')); - $this->assertTrue(isset($info['bundles'][$new_name]), t('The new bundle name appears in entity_get_info().')); + $this->assertFalse(isset($info['bundles'][$old_name]), 'The old bundle name does not appear in entity_get_info().'); + $this->assertTrue(isset($info['bundles'][$new_name]), 'The new bundle name appears in entity_get_info().'); // Check that the field instance is still attached to the vocabulary. $field = field_info_field($this->field_name); $allowed_values = $field['settings']['allowed_values']; - $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, t('Index 0: Machine name was updated correctly.')); - $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, t('Index 1: Machine name was updated correctly.')); - $this->assertEqual($allowed_values[2]['vocabulary'], 'foo', t('Index 2: Machine name was left untouched.')); + $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, 'Index 0: Machine name was updated correctly.'); + $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, 'Index 1: Machine name was updated correctly.'); + $this->assertEqual($allowed_values[2]['vocabulary'], 'foo', 'Index 2: Machine name was left untouched.'); } + } /** * Tests a taxonomy term reference field that allows multiple vocabularies. */ class TaxonomyTermFieldMultipleVocabularyTestCase extends TaxonomyWebTestCase { + protected $instance; protected $vocabulary1; protected $vocabulary2; public static function getInfo() { return array( - 'name' => 'Multiple vocabulary term reference field', - 'description' => 'Tests term reference fields that allow multiple vocabularies.', + 'name' => 'Multiple vocabulary term reference field', + 'description' => 'Tests term reference fields that allow multiple vocabularies.', 'group' => 'Taxonomy', ); } @@ -1638,7 +1648,7 @@ class TaxonomyTermFieldMultipleVocabularyTestCase extends TaxonomyWebTestCase { // Submit an entity with both terms. $langcode = LANGUAGE_NONE; $this->drupalGet('test-entity/add/test-bundle'); - $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is displayed'); + $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is displayed.'); $edit = array( "{$this->field_name}[$langcode][]" => array($term1->tid, $term2->tid), ); @@ -1677,7 +1687,7 @@ class TaxonomyTermFieldMultipleVocabularyTestCase extends TaxonomyWebTestCase { // The widget should still be displayed. $this->drupalGet('test-entity/add/test-bundle'); - $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is still displayed'); + $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is still displayed.'); // Term 1 should still pass validation. $edit = array( @@ -1685,13 +1695,14 @@ class TaxonomyTermFieldMultipleVocabularyTestCase extends TaxonomyWebTestCase { ); $this->drupalPost(NULL, $edit, t('Save')); } -} +} /** * Test taxonomy token replacement in strings. */ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { + public static function getInfo() { return array( 'name' => 'Taxonomy token replacement', @@ -1772,7 +1783,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { foreach ($tests as $input => $expected) { $output = token_replace($input, array('term' => $term1), array('language' => $language)); - $this->assertEqual($output, $expected, t('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); } // Generate and test sanitized tokens for term2. @@ -1788,11 +1799,11 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name); // Test to make sure that we generated something for each token. - $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.')); + $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.'); foreach ($tests as $input => $expected) { $output = token_replace($input, array('term' => $term2), array('language' => $language)); - $this->assertEqual($output, $expected, t('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); } // Generate and test unsanitized tokens. @@ -1803,7 +1814,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { foreach ($tests as $input => $expected) { $output = token_replace($input, array('term' => $term2), array('language' => $language, 'sanitize' => FALSE)); - $this->assertEqual($output, $expected, t('Unsanitized taxonomy term token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Unsanitized taxonomy term token %token replaced.', array('%token' => $input))); } // Generate and test sanitized tokens. @@ -1815,11 +1826,11 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $tests['[vocabulary:term-count]'] = 2; // Test to make sure that we generated something for each token. - $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.')); + $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.'); foreach ($tests as $input => $expected) { $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language)); - $this->assertEqual($output, $expected, t('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Sanitized taxonomy vocabulary token %token replaced.', array('%token' => $input))); } // Generate and test unsanitized tokens. @@ -1828,15 +1839,17 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { foreach ($tests as $input => $expected) { $output = token_replace($input, array('vocabulary' => $this->vocabulary), array('language' => $language, 'sanitize' => FALSE)); - $this->assertEqual($output, $expected, t('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input))); + $this->assertEqual($output, $expected, format_string('Unsanitized taxonomy vocabulary token %token replaced.', array('%token' => $input))); } } + } /** * Tests for verifying that taxonomy pages use the correct theme. */ class TaxonomyThemeTestCase extends TaxonomyWebTestCase { + public static function getInfo() { return array( 'name' => 'Taxonomy theme switching', @@ -1867,15 +1880,70 @@ class TaxonomyThemeTestCase extends TaxonomyWebTestCase { // should use the administrative theme. $vocabulary = $this->createVocabulary(); $this->drupalGet('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add'); - $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page for adding a taxonomy term.")); + $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page for adding a taxonomy term."); // Viewing a taxonomy term should use the default theme. $term = $this->createTerm($vocabulary); $this->drupalGet('taxonomy/term/' . $term->tid); - $this->assertRaw('bartik/css/style.css', t("The default theme's CSS appears on the page for viewing a taxonomy term.")); + $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page for viewing a taxonomy term."); // Editing a taxonomy term should use the same theme as adding one. $this->drupalGet('taxonomy/term/' . $term->tid . '/edit'); - $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page for editing a taxonomy term.")); + $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page for editing a taxonomy term."); + } + +} + +/** + * Tests the functionality of EntityFieldQuery for taxonomy entities. + */ +class TaxonomyEFQTestCase extends TaxonomyWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Taxonomy EntityFieldQuery', + 'description' => 'Verifies operation of a taxonomy-based EntityFieldQuery.', + 'group' => 'Taxonomy', + ); + } + + function setUp() { + parent::setUp(); + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy')); + $this->drupalLogin($this->admin_user); + $this->vocabulary = $this->createVocabulary(); } + + /** + * Tests that a basic taxonomy EntityFieldQuery works. + */ + function testTaxonomyEFQ() { + $terms = array(); + for ($i = 0; $i < 5; $i++) { + $term = $this->createTerm($this->vocabulary); + $terms[$term->tid] = $term; + } + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'taxonomy_term'); + $result = $query->execute(); + $result = $result['taxonomy_term']; + asort($result); + $this->assertEqual(array_keys($terms), array_keys($result), 'Taxonomy terms were retrieved by EntityFieldQuery.'); + + // Create a second vocabulary and five more terms. + $vocabulary2 = $this->createVocabulary(); + $terms2 = array(); + for ($i = 0; $i < 5; $i++) { + $term = $this->createTerm($vocabulary2); + $terms2[$term->tid] = $term; + } + + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'taxonomy_term'); + $query->entityCondition('bundle', $vocabulary2->machine_name); + $result = $query->execute(); + $result = $result['taxonomy_term']; + asort($result); + $this->assertEqual(array_keys($terms2), array_keys($result), format_string('Taxonomy terms from the %name vocabulary were retrieved by EntityFieldQuery.', array('%name' => $vocabulary2->name))); + } + } diff --git a/modules/toolbar/toolbar.info b/modules/toolbar/toolbar.info index eaddba5d7..d086d8661 100644 --- a/modules/toolbar/toolbar.info +++ b/modules/toolbar/toolbar.info @@ -4,8 +4,8 @@ core = 7.x package = Core version = VERSION -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/tracker/tracker.info b/modules/tracker/tracker.info index f6a4069f2..f661a0cde 100644 --- a/modules/tracker/tracker.info +++ b/modules/tracker/tracker.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = tracker.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/tracker/tracker.install b/modules/tracker/tracker.install index 84305d822..9967b9db4 100644 --- a/modules/tracker/tracker.install +++ b/modules/tracker/tracker.install @@ -221,5 +221,5 @@ function tracker_update_7000() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/translation/tests/translation_test.info b/modules/translation/tests/translation_test.info index 2a08ab218..39b91c1eb 100644 --- a/modules/translation/tests/translation_test.info +++ b/modules/translation/tests/translation_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/translation/translation.info b/modules/translation/translation.info index 6726a857e..dbb0740c6 100644 --- a/modules/translation/translation.info +++ b/modules/translation/translation.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = translation.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/translation/translation.module b/modules/translation/translation.module index e61be1d66..331235705 100644 --- a/modules/translation/translation.module +++ b/modules/translation/translation.module @@ -84,7 +84,7 @@ function translation_menu() { * @see translation_menu() */ function _translation_tab_access($node) { - if ($node->language != LANGUAGE_NONE && translation_supported_type($node->type) && node_access('view', $node)) { + if (entity_language('node', $node) != LANGUAGE_NONE && translation_supported_type($node->type) && node_access('view', $node)) { return user_access('translate content'); } return FALSE; @@ -233,7 +233,7 @@ function translation_node_view($node, $view_mode) { foreach ($translations as $langcode => $translation) { // Do not show links to the same node, to unpublished translations or to // translations in disabled languages. - if ($translation->status && isset($languages[$langcode]) && $langcode != $node->language) { + if ($translation->status && isset($languages[$langcode]) && $langcode != entity_language('node', $node)) { $language = $languages[$langcode]; $key = "translation_$langcode"; @@ -313,7 +313,7 @@ function translation_node_prepare($node) { // Add field translations and let other modules module add custom translated // fields. - field_attach_prepare_translation('node', $node, $node->language, $source_node, $source_node->language); + field_attach_prepare_translation('node', $node, $langcode, $source_node, $source_node->language); } } @@ -358,7 +358,8 @@ function translation_node_insert($node) { function translation_node_update($node) { // Only act if we are dealing with a content type supporting translations. if (translation_supported_type($node->type)) { - if (isset($node->translation) && $node->translation && !empty($node->language) && $node->tnid) { + $langcode = entity_language('node', $node); + if (isset($node->translation) && $node->translation && !empty($langcode) && $node->tnid) { // Update translation information. db_update('node') ->fields(array( @@ -389,7 +390,8 @@ function translation_node_validate($node, $form) { if (translation_supported_type($node->type) && (!empty($node->tnid) || !empty($form['#node']->translation_source->nid))) { $tnid = !empty($node->tnid) ? $node->tnid : $form['#node']->translation_source->nid; $translations = translation_node_get_translations($tnid); - if (isset($translations[$node->language]) && $translations[$node->language]->nid != $node->nid ) { + $langcode = entity_language('node', $node); + if (isset($translations[$langcode]) && $translations[$langcode]->nid != $node->nid ) { form_set_error('language', t('There is already a translation in this language.')); } } @@ -469,7 +471,8 @@ function translation_node_get_translations($tnid) { ->execute(); foreach ($result as $node) { - $translations[$tnid][$node->language] = $node; + $langcode = entity_language('node', $node); + $translations[$tnid][$langcode] = $node; } } return $translations[$tnid]; @@ -523,10 +526,11 @@ function translation_language_switch_links_alter(array &$links, $type, $path) { // have translations it might be a language neutral node, in which case we // must leave the language switch links unaltered. This is true also for // nodes not having translation support enabled. - if (empty($node) || $node->language == LANGUAGE_NONE || !translation_supported_type($node->type)) { + if (empty($node) || entity_language('node', $node) == LANGUAGE_NONE || !translation_supported_type($node->type)) { return; } - $translations = array($node->language => $node); + $langcode = entity_language('node', $node); + $translations = array($langcode => $node); } else { $translations = translation_node_get_translations($node->tnid); diff --git a/modules/translation/translation.pages.inc b/modules/translation/translation.pages.inc index fa4070bb8..110fea603 100644 --- a/modules/translation/translation.pages.inc +++ b/modules/translation/translation.pages.inc @@ -27,7 +27,7 @@ function translation_node_overview($node) { else { // We have no translation source nid, this could be a new set, emulate that. $tnid = $node->nid; - $translations = array($node->language => $node); + $translations = array(entity_language('node', $node) => $node); } $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE); diff --git a/modules/translation/translation.test b/modules/translation/translation.test index 09bc9e3b9..e64f9cb86 100644 --- a/modules/translation/translation.test +++ b/modules/translation/translation.test @@ -429,7 +429,7 @@ class TranslationTestCase extends DrupalWebTestCase { $result = TRUE; $languages = language_list(); - $page_language = $languages[$node->language]; + $page_language = $languages[entity_language('node', $node)]; $translation_language = $languages[$translation->language]; $url = url("node/$translation->nid", array('language' => $translation_language)); diff --git a/modules/trigger/tests/trigger_test.info b/modules/trigger/tests/trigger_test.info index e0a3ddbf5..3f9dd05d7 100644 --- a/modules/trigger/tests/trigger_test.info +++ b/modules/trigger/tests/trigger_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/trigger/trigger.admin.inc b/modules/trigger/trigger.admin.inc index 447d0dc07..5b607c85d 100644 --- a/modules/trigger/trigger.admin.inc +++ b/modules/trigger/trigger.admin.inc @@ -53,7 +53,7 @@ function trigger_assign($module_to_display = NULL) { * @ingroup forms */ function trigger_unassign($form, $form_state, $module, $hook = NULL, $aid = NULL) { - if (!($hook && $aid)) { + if (!isset($hook, $aid)) { drupal_goto('admin/structure/trigger'); } diff --git a/modules/trigger/trigger.info b/modules/trigger/trigger.info index 753711b12..c6e7ee5fe 100644 --- a/modules/trigger/trigger.info +++ b/modules/trigger/trigger.info @@ -6,8 +6,8 @@ core = 7.x files[] = trigger.test configure = admin/structure/trigger -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/trigger/trigger.install b/modules/trigger/trigger.install index 20d5c3a3d..779cd2aad 100644 --- a/modules/trigger/trigger.install +++ b/modules/trigger/trigger.install @@ -79,6 +79,11 @@ function trigger_update_7000() { db_add_primary_key('trigger_assignments', array('hook', 'aid')); } +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Increase the length of the "hook" field to 78 characters. * @@ -105,3 +110,7 @@ function trigger_update_7002() { ->execute(); } } + +/** + * @} End of "addtogroup updates-7.x-extra". + */ diff --git a/modules/trigger/trigger.module b/modules/trigger/trigger.module index 82d117272..aeb1211c9 100644 --- a/modules/trigger/trigger.module +++ b/modules/trigger/trigger.module @@ -65,13 +65,35 @@ function trigger_menu() { 'description' => 'Unassign an action from a trigger.', 'page callback' => 'drupal_get_form', 'page arguments' => array('trigger_unassign'), - 'access arguments' => array('administer actions'), + // Only accessible if there are any actions that can be unassigned. + 'access callback' => 'trigger_menu_unassign_access', + // Only output in the breadcrumb, not in menu trees. + 'type' => MENU_VISIBLE_IN_BREADCRUMB, 'file' => 'trigger.admin.inc', ); return $items; } +/** + * Access callback: Determines if triggers can be unassigned. + * + * @return bool + * TRUE if there are triggers that the user can unassign, FALSE otherwise. + * + * @see trigger_menu() + */ +function trigger_menu_unassign_access() { + if (!user_access('administer actions')) { + return FALSE; + } + $count = db_select('trigger_assignments') + ->countQuery() + ->execute() + ->fetchField(); + return $count > 0; +} + /** * Implements hook_trigger_info(). * diff --git a/modules/trigger/trigger.test b/modules/trigger/trigger.test index 138de6281..829e1898b 100644 --- a/modules/trigger/trigger.test +++ b/modules/trigger/trigger.test @@ -740,3 +740,32 @@ class TriggerOrphanedActionsTestCase extends DrupalWebTestCase { $this->assertRaw(t('!post %title has been updated.', array('!post' => 'Basic page', '%title' => $edit["title"])), t('Make sure the Basic page can be updated with the missing trigger function.')); } } + +/** + * Tests the unassigning of triggers. + */ +class TriggerUnassignTestCase extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Trigger unassigning', + 'description' => 'Tests the unassigning of triggers.', + 'group' => 'Trigger', + ); + } + + function setUp() { + parent::setUp('trigger', 'trigger_test'); + $web_user = $this->drupalCreateUser(array('administer actions')); + $this->drupalLogin($web_user); + } + + /** + * Tests an attempt to unassign triggers when none are assigned. + */ + function testUnassignAccessDenied() { + $this->drupalGet('admin/structure/trigger/unassign'); + $this->assertResponse(403, 'If there are no actions available, return access denied.'); + } + +} diff --git a/modules/update/tests/aaa_update_test.info b/modules/update/tests/aaa_update_test.info index a2eb6dc3d..9d5f624e7 100644 --- a/modules/update/tests/aaa_update_test.info +++ b/modules/update/tests/aaa_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/tests/bbb_update_test.info b/modules/update/tests/bbb_update_test.info index e8f8f856a..bec9aae16 100644 --- a/modules/update/tests/bbb_update_test.info +++ b/modules/update/tests/bbb_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/tests/ccc_update_test.info b/modules/update/tests/ccc_update_test.info index aac3a7b63..ad559a307 100644 --- a/modules/update/tests/ccc_update_test.info +++ b/modules/update/tests/ccc_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info b/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info index eb631e810..81022b26c 100644 --- a/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info +++ b/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info @@ -3,8 +3,8 @@ description = Test theme which acts as a base theme for other test subthemes. core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info b/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info index c097d5bc1..f9663e429 100644 --- a/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info +++ b/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info @@ -4,8 +4,8 @@ core = 7.x base theme = update_test_basetheme hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/tests/update_test.info b/modules/update/tests/update_test.info index ddcc2b5c2..ca1f9be42 100644 --- a/modules/update/tests/update_test.info +++ b/modules/update/tests/update_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/tests/update_test.module b/modules/update/tests/update_test.module index e7ee43eec..6fe4bddea 100644 --- a/modules/update/tests/update_test.module +++ b/modules/update/tests/update_test.module @@ -1,5 +1,10 @@ <?php +/** + * @file + * Module for testing Update Manager functionality. + */ + /** * Implements hook_system_theme_info(). */ @@ -34,13 +39,13 @@ function update_test_menu() { /** * Implements hook_system_info_alter(). * - * This checks the 'update_test_system_info' variable and sees if we need to - * alter the system info for the given $file based on the setting. The setting - * is expected to be a nested associative array. If the key '#all' is defined, - * its subarray will include .info keys and values for all modules and themes - * on the system. Otherwise, the settings array is keyed by the module or - * theme short name ($file->name) and the subarrays contain settings just for - * that module or theme. + * Checks the 'update_test_system_info' variable and sees if we need to alter + * the system info for the given $file based on the setting. The setting is + * expected to be a nested associative array. If the key '#all' is defined, its + * subarray will include .info keys and values for all modules and themes on the + * system. Otherwise, the settings array is keyed by the module or theme short + * name ($file->name) and the subarrays contain settings just for that module or + * theme. */ function update_test_system_info_alter(&$info, $file) { $setting = variable_get('update_test_system_info', array()); @@ -56,13 +61,12 @@ function update_test_system_info_alter(&$info, $file) { /** * Implements hook_update_status_alter(). * - * This checks the 'update_test_update_status' variable and sees if we need to - * alter the update status for the given project based on the setting. The - * setting is expected to be a nested associative array. If the key '#all' is - * defined, its subarray will include .info keys and values for all modules - * and themes on the system. Otherwise, the settings array is keyed by the - * module or theme short name and the subarrays contain settings just for that - * module or theme. + * Checks the 'update_test_update_status' variable and sees if we need to alter + * the update status for the given project based on the setting. The setting is + * expected to be a nested associative array. If the key '#all' is defined, its + * subarray will include .info keys and values for all modules and themes on the + * system. Otherwise, the settings array is keyed by the module or theme short + * name and the subarrays contain settings just for that module or theme. */ function update_test_update_status_alter(&$projects) { $setting = variable_get('update_test_update_status', array()); @@ -80,18 +84,20 @@ function update_test_update_status_alter(&$projects) { } /** - * Page callback, prints mock XML for the update module. + * Page callback: Prints mock XML for the Update Manager module. * * The specific XML file to print depends on two things: the project we're * trying to fetch data for, and the desired "availability scenario" for that - * project which we're trying to test. Before attempting to fetch this data - * (by checking for updates on the available updates report), callers need to - * define the 'update_test_xml_map' variable as an array, keyed by project - * name, indicating which availability scenario to use for that project. + * project which we're trying to test. Before attempting to fetch this data (by + * checking for updates on the available updates report), callers need to define + * the 'update_test_xml_map' variable as an array, keyed by project name, + * indicating which availability scenario to use for that project. * * @param $project_name - * The project short name update.module is trying to fetch data for (the + * The project short name the update manager is trying to fetch data for (the * fetch URLs are of the form: [base_url]/[project_name]/[core_version]). + * + * @see update_test_menu() */ function update_test_mock_page($project_name) { $xml_map = variable_get('update_test_xml_map', FALSE); @@ -115,7 +121,7 @@ function update_test_mock_page($project_name) { } /** - * Implement hook_archiver_info(). + * Implements hook_archiver_info(). */ function update_test_archiver_info() { return array( @@ -147,13 +153,23 @@ function update_test_filetransfer_info() { } /** - * Mock FileTransfer object to test the settings form functionality. + * Mocks a FileTransfer object to test the settings form functionality. */ class UpdateTestFileTransfer { + + /** + * Returns an UpdateTestFileTransfer object. + * + * @return + * A new UpdateTestFileTransfer object. + */ public static function factory() { return new UpdateTestFileTransfer; } + /** + * Returns a settings form with a text field to input a username. + */ public function getSettingsForm() { $form = array(); $form['udpate_test_username'] = array( @@ -165,7 +181,9 @@ class UpdateTestFileTransfer { } /** - * Return an Error 503 (Service unavailable) page. + * Page callback: Displays an Error 503 (Service unavailable) page. + * + * @see update_test_menu() */ function update_callback_service_unavailable() { drupal_add_http_header('Status', '503 Service unavailable'); diff --git a/modules/update/update-rtl.css b/modules/update/update-rtl.css index 5fc83d1a6..f181c8454 100644 --- a/modules/update/update-rtl.css +++ b/modules/update/update-rtl.css @@ -1,3 +1,7 @@ +/** + * @file + * RTL styles used by the Update Manager module. + */ .update .project { padding-right: .25em; diff --git a/modules/update/update.api.php b/modules/update/update.api.php index 87f95cac5..cb5669e32 100644 --- a/modules/update/update.api.php +++ b/modules/update/update.api.php @@ -2,7 +2,7 @@ /** * @file - * Hooks provided by the Update Status module. + * Hooks provided by the Update Manager module. */ /** @@ -14,23 +14,22 @@ * Alter the list of projects before fetching data and comparing versions. * * Most modules will never need to implement this hook. It is for advanced - * interaction with the update status module: mere mortals need not apply. - * The primary use-case for this hook is to add projects to the list, for - * example, to provide update status data on disabled modules and themes. A - * contributed module might want to hide projects from the list, for example, - * if there is a site-specific module that doesn't have any official releases, - * that module could remove itself from this list to avoid "No available - * releases found" warnings on the available updates report. In rare cases, a - * module might want to alter the data associated with a project already in - * the list. + * interaction with the Update Manager module. The primary use-case for this + * hook is to add projects to the list; for example, to provide update status + * data on disabled modules and themes. A contributed module might want to hide + * projects from the list; for example, if there is a site-specific module that + * doesn't have any official releases, that module could remove itself from this + * list to avoid "No available releases found" warnings on the available updates + * report. In rare cases, a module might want to alter the data associated with + * a project already in the list. * * @param $projects * Reference to an array of the projects installed on the system. This - * includes all the metadata documented in the comments below for each - * project (either module or theme) that is currently enabled. The array is - * initially populated inside update_get_projects() with the help of - * _update_process_info_list(), so look there for examples of how to - * populate the array with real values. + * includes all the metadata documented in the comments below for each project + * (either module or theme) that is currently enabled. The array is initially + * populated inside update_get_projects() with the help of + * _update_process_info_list(), so look there for examples of how to populate + * the array with real values. * * @see update_get_projects() * @see _update_process_info_list() @@ -118,6 +117,7 @@ function hook_update_status_alter(&$projects) { * no problems, return an empty array. * * @see update_manager_archive_verify() + * @ingroup update_manager_file */ function hook_verify_update_archive($project, $archive_file, $directory) { $errors = array(); diff --git a/modules/update/update.authorize.inc b/modules/update/update.authorize.inc index c9b76dd30..6ddd2c53a 100644 --- a/modules/update/update.authorize.inc +++ b/modules/update/update.authorize.inc @@ -2,15 +2,19 @@ /** * @file - * Callbacks and related functions invoked by authorize.php to update projects - * on the Drupal site. We use the Batch API to actually update each individual - * project on the site. All of the code in this file is run at a low bootstrap - * level (modules are not loaded), so these functions cannot assume access to - * the rest of the update module code. + * Callbacks and related functions invoked by authorize.php to update projects. + * + * We use the Batch API to actually update each individual project on the site. + * All of the code in this file is run at a low bootstrap level (modules are not + * loaded), so these functions cannot assume access to the rest of the code of + * the Update Manager module. */ /** - * Callback invoked by authorize.php to update existing projects. + * Updates existing projects when invoked by authorize.php. + * + * Callback for system_authorized_init() in + * update_manager_update_ready_form_submit(). * * @param $filetransfer * The FileTransfer object created by authorize.php for use during this @@ -18,9 +22,9 @@ * @param $projects * 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. - * - 'local_url': The locally installed location of new code to update with. + * - project: The canonical project short name. + * - updater_name: The name of the 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) { $operations = array(); @@ -50,13 +54,16 @@ function update_authorize_run_update($filetransfer, $projects) { } /** - * Callback invoked by authorize.php to install a new project. + * Installs a new project when invoked by authorize.php. + * + * Callback for system_authorized_init() in + * update_manager_install_form_submit(). * * @param FileTransfer $filetransfer * The FileTransfer object created by authorize.php for use during this * operation. * @param string $project - * The canonical project short name (e.g. {system}.name). + * 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. * @param string $local_url @@ -90,7 +97,7 @@ function update_authorize_run_install($filetransfer, $project, $updater_name, $l } /** - * Copy a project to its proper place when authorized with elevated privileges. + * Batch callback: Copies project to its proper place when authorized to do so. * * @param string $project * The canonical short name of the project being installed. @@ -102,7 +109,7 @@ function update_authorize_run_install($filetransfer, $project, $updater_name, $l * @param FileTransfer $filetransfer * The FileTransfer object to use for performing this operation. * @param array $context - * Reference to an array used for BatchAPI storage. + * Reference to an array used for Batch API storage. */ function update_authorize_batch_copy_project($project, $updater_name, $local_url, $filetransfer, &$context) { @@ -118,15 +125,13 @@ function update_authorize_batch_copy_project($project, $updater_name, $local_url $context['results']['tasks'] = array(); } - /** - * The batch API uses a session, and since all the arguments are serialized - * and unserialized between requests, although the FileTransfer object - * itself will be reconstructed, the connection pointer itself will be lost. - * However, the FileTransfer object will still have the connection variable, - * even though the connection itself is now gone. So, although it's ugly, we - * have to unset the connection variable at this point so that the - * FileTransfer object will re-initiate the actual connection. - */ + // The batch API uses a session, and since all the arguments are serialized + // and unserialized between requests, although the FileTransfer object itself + // will be reconstructed, the connection pointer itself will be lost. However, + // the FileTransfer object will still have the connection variable, even + // though the connection itself is now gone. So, although it's ugly, we have + // to unset the connection variable at this point so that the FileTransfer + // object will re-initiate the actual connection. unset($filetransfer->connection); if (!empty($context['results']['log'][$project]['#abort'])) { @@ -163,11 +168,16 @@ function update_authorize_batch_copy_project($project, $updater_name, $local_url } /** - * Batch callback for when the authorized update batch is finished. + * Batch callback: Performs actions when the authorized update batch is done. * * This processes the results and stashes them into SESSION such that * authorize.php will render a report. Also responsible for putting the site * back online and clearing the update status cache after a successful update. + * + * @param $success + * TRUE if the batch operation was successful; FALSE if there were errors. + * @param $results + * An associative array of results from the batch operation. */ function update_authorize_update_batch_finished($success, $results) { foreach ($results['log'] as $project => $messages) { @@ -225,11 +235,16 @@ function update_authorize_update_batch_finished($success, $results) { } /** - * Batch callback for when the authorized install batch is finished. + * Batch callback: Performs actions when the authorized install batch is done. * * This processes the results and stashes them into SESSION such that * authorize.php will render a report. Also responsible for putting the site * back online after a successful install if necessary. + * + * @param $success + * TRUE if the batch operation was a success; FALSE if there were errors. + * @param $results + * An associative array of results from the batch operation. */ function update_authorize_install_batch_finished($success, $results) { foreach ($results['log'] as $project => $messages) { @@ -279,26 +294,30 @@ function update_authorize_install_batch_finished($success, $results) { } /** - * Helper function to create a structure of log messages. + * Creates a structure of log messages. * * @param array $project_results + * An associative array of results from the batch operation. * @param string $message + * A string containing a log message. * @param bool $success + * (optional) TRUE if the operation the message is about was a success, FALSE + * if there were errors. Defaults to TRUE. */ function _update_batch_create_message(&$project_results, $message, $success = TRUE) { $project_results[] = array('message' => $message, 'success' => $success); } /** - * Private helper function to clear cached available update status data. + * Clears cached available update status data. * - * Since this function is run at such a low bootstrap level, update.module is - * not loaded. So, we can't just call _update_cache_clear(). However, the - * database is bootstrapped, so we can do a query ourselves to clear out what - * we want to clear. + * Since this function is run at such a low bootstrap level, the Update Manager + * module is not loaded. So, we can't just call _update_cache_clear(). However, + * the database is bootstrapped, so we can do a query ourselves to clear out + * what we want to clear. * - * Note that we do not want to just truncate the table, since that would - * remove items related to currently pending fetch attempts. + * Note that we do not want to just truncate the table, since that would remove + * items related to currently pending fetch attempts. * * @see update_authorize_update_batch_finished() * @see _update_cache_clear() diff --git a/modules/update/update.compare.inc b/modules/update/update.compare.inc index 2ccd97c0e..cd8b762d0 100644 --- a/modules/update/update.compare.inc +++ b/modules/update/update.compare.inc @@ -6,7 +6,7 @@ */ /** - * Fetch an array of installed and enabled projects. + * Fetches an array of installed and enabled projects. * * This is only responsible for generating an array of projects (taking into * account projects that include more than one module or theme). Other @@ -15,14 +15,39 @@ * that logic is only required when preparing the status report, not for * fetching the available release data. * - * This array is fairly expensive to construct, since it involves a lot of - * disk I/O, so we cache the results into the {cache_update} table using the - * 'update_project_projects' cache ID. However, since this is not the data - * about available updates fetched from the network, it is ok to invalidate it - * somewhat quickly. If we keep this data for very long, site administrators - * are more likely to see incorrect results if they upgrade to a newer version - * of a module or theme but do not visit certain pages that automatically - * clear this cache. + * This array is fairly expensive to construct, since it involves a lot of disk + * I/O, so we cache the results into the {cache_update} table using the + * 'update_project_projects' cache ID. However, since this is not the data about + * available updates fetched from the network, it is acceptable to invalidate it + * somewhat quickly. If we keep this data for very long, site administrators are + * more likely to see incorrect results if they upgrade to a newer version of a + * module or theme but do not visit certain pages that automatically clear this + * cache. + * + * @return + * An associative array of currently enabled projects keyed by the + * machine-readable project short name. Each project contains: + * - name: The machine-readable project short name. + * - info: An array with values from the main .info file for this project. + * - name: The human-readable name of the project. + * - package: The package that the project is grouped under. + * - version: The version of the project. + * - project: The Drupal.org project name. + * - datestamp: The date stamp of the project's main .info file. + * - _info_file_ctime: The maximum file change time for all of the .info + * files included in this project. + * - datestamp: The date stamp when the project was released, if known. + * - includes: An associative array containing all projects included with this + * project, keyed by the machine-readable short name with the human-readable + * name as value. + * - project_type: The type of project. Allowed values are 'module' and + * 'theme'. + * - project_status: This indicates if the project is enabled and will always + * be TRUE, as the function only returns enabled projects. + * - sub_themes: If the project is a theme it contains an associative array of + * all sub-themes. + * - base_themes: If the project is a theme it contains an associative array + * of all base-themes. * * @see update_process_project_info() * @see update_calculate_project_data() @@ -53,25 +78,25 @@ function update_get_projects() { } /** - * Populate an array of project data. - * - * This iterates over a list of the installed modules or themes and groups - * them by project and status. A few parts of this function assume that - * enabled modules and themes are always processed first, and if disabled - * modules or themes are being processed (there is a setting to control if - * disabled code should be included in the Available updates report or not), - * those are only processed after $projects has been populated with - * information about the enabled code. 'Hidden' modules and themes are always - * ignored. This function also records the latest change time on the .info - * files for each module or theme, which is important data which is used when - * deciding if the cached available update data should be invalidated. + * Populates an array of project data. + * + * This iterates over a list of the installed modules or themes and groups them + * by project and status. A few parts of this function assume that enabled + * modules and themes are always processed first, and if disabled modules or + * themes are being processed (there is a setting to control if disabled code + * should be included or not in the 'Available updates' report), those are only + * processed after $projects has been populated with information about the + * enabled code. Modules and themes set as hidden are always ignored. This + * function also records the latest change time on the .info files for each + * module or theme, which is important data which is used when deciding if the + * cached available update data should be invalidated. * * @param $projects * Reference to the array of project data of what's installed on this site. * @param $list * Array of data to process to add the relevant info to the $projects array. * @param $project_type - * The kind of data in the list (can be 'module' or 'theme'). + * The kind of data in the list. Can be 'module' or 'theme'. * @param $status * Boolean that controls what status (enabled or disabled) to process out of * the $list and add to the $projects array. @@ -211,8 +236,13 @@ function _update_process_info_list(&$projects, $list, $project_type, $status) { } /** - * Given a $file object (as returned by system_get_files_database()), figure - * out what project it belongs to. + * Determines what project a given file object belongs to. + * + * @param $file + * A file object as returned by system_get_files_database(). + * + * @return + * The canonical project short name. * * @see system_get_files_database() */ @@ -228,7 +258,9 @@ function update_get_project_name($file) { } /** - * Process the list of projects on the system to figure out the currently + * Determines version and type information for currently installed projects. + * + * Processes the list of projects on the system to figure out the currently * installed versions, and other information that is required before we can * compare against the available releases to produce the status report. * @@ -277,7 +309,7 @@ function update_process_project_info(&$projects) { } /** - * Calculate the current update status of all projects on the site. + * Calculates the current update status of all projects on the site. * * The results of this function are expensive to compute, especially on sites * with lots of modules or themes, since it involves a lot of comparisons and @@ -285,13 +317,16 @@ function update_process_project_info(&$projects) { * table using the 'update_project_data' cache ID. However, since this is not * the data about available updates fetched from the network, it is ok to * invalidate it somewhat quickly. If we keep this data for very long, site - * administrators are more likely to see incorrect results if they upgrade to - * a newer version of a module or theme but do not visit certain pages that + * administrators are more likely to see incorrect results if they upgrade to a + * newer version of a module or theme but do not visit certain pages that * automatically clear this cache. * * @param array $available * Data about available project releases. * + * @return + * An array of installed projects with current update status information. + * * @see update_get_available() * @see update_get_projects() * @see update_process_project_info() @@ -327,52 +362,50 @@ function update_calculate_project_data($available) { } /** - * Calculate the current update status of a specific project. + * Calculates the current update status of a specific project. * - * This function is the heart of the update status feature. For each project - * it is invoked with, it first checks if the project has been flagged with a + * This function is the heart of the update status feature. For each project it + * is invoked with, it first checks if the project has been flagged with a * special status like "unsupported" or "insecure", or if the project node * itself has been unpublished. In any of those cases, the project is marked * with an error and the next project is considered. * * If the project itself is valid, the function decides what major release * series to consider. The project defines what the currently supported major - * versions are for each version of core, so the first step is to make sure - * the current version is still supported. If so, that's the target version. - * If the current version is unsupported, the project maintainer's recommended - * major version is used. There's also a check to make sure that this function - * never recommends an earlier release than the currently installed major - * version. - * - * Given a target major version, it scans the available releases looking for + * versions are for each version of core, so the first step is to make sure the + * current version is still supported. If so, that's the target version. If the + * current version is unsupported, the project maintainer's recommended major + * version is used. There's also a check to make sure that this function never + * recommends an earlier release than the currently installed major version. + * + * Given a target major version, the available releases are scanned looking for * the specific release to recommend (avoiding beta releases and development - * snapshots if possible). This is complicated to describe, but an example - * will help clarify. For the target major version, find the highest patch - * level. If there is a release at that patch level with no extra ("beta", - * etc), then we recommend the release at that patch level with the most - * recent release date. If every release at that patch level has extra (only - * betas), then recommend the latest release from the previous patch - * level. For example: + * snapshots if possible). For the target major version, the highest patch level + * is found. If there is a release at that patch level with no extra ("beta", + * etc.), then the release at that patch level with the most recent release date + * is recommended. If every release at that patch level has extra (only betas), + * then the latest release from the previous patch level is recommended. For + * example: * - * 1.6-bugfix <-- recommended version because 1.6 already exists. - * 1.6 + * - 1.6-bugfix <-- recommended version because 1.6 already exists. + * - 1.6 * * or * - * 1.6-beta - * 1.5 <-- recommended version because no 1.6 exists. - * 1.4 + * - 1.6-beta + * - 1.5 <-- recommended version because no 1.6 exists. + * - 1.4 * - * It also looks for the latest release from the same major version, even a - * beta release, to display to the user as the "Latest version" option. - * Additionally, it finds the latest official release from any higher major - * versions that have been released to provide a set of "Also available" + * Also, the latest release from the same major version is looked for, even beta + * releases, to display to the user as the "Latest version" option. + * Additionally, the latest official release from any higher major versions that + * have been released is searched for to provide a set of "Also available" * options. * - * Finally, and most importantly, it keeps scanning the release history until - * it gets to the currently installed release, searching for anything marked - * as a security update. If any security updates have been found between the - * recommended release and the installed version, all of the releases that + * Finally, and most importantly, the release history continues to be scanned + * until the currently installed release is reached, searching for anything + * marked as a security update. If any security updates have been found between + * the recommended release and the installed version, all of the releases that * included a security fix are recorded so that the site administrator can be * warned their site is insecure, and links pointing to the release notes for * each security update can be included (which, in turn, will link to the @@ -381,9 +414,15 @@ function update_calculate_project_data($available) { * This function relies on the fact that the .xml release history data comes * sorted based on major version and patch level, then finally by release date * if there are multiple releases such as betas from the same major.patch - * version (e.g. 5.x-1.5-beta1, 5.x-1.5-beta2, and 5.x-1.5). Development + * version (e.g., 5.x-1.5-beta1, 5.x-1.5-beta2, and 5.x-1.5). Development * snapshots for a given major version are always listed last. * + * @param $project + * An array containing information about a specific project. + * @param $project_data + * An array containing information about a specific project. + * @param $available + * Data about available project releases of a specific project. */ function update_calculate_project_update_status($project, &$project_data, $available) { foreach (array('title', 'link') as $attribute) { @@ -707,16 +746,16 @@ function update_calculate_project_update_status($project, &$project_data, $avail } /** - * Retrieve data from {cache_update} or empty the cache when necessary. + * Retrieves data from {cache_update} or empties the cache when necessary. * * Two very expensive arrays computed by this module are the list of all - * installed modules and themes (and .info data, project associations, etc), - * and the current status of the site relative to the currently available - * releases. These two arrays are cached in the {cache_update} table and used - * whenever possible. The cache is cleared whenever the administrator visits - * the status report, available updates report, or the module or theme - * administration pages, since we should always recompute the most current - * values on any of those pages. + * installed modules and themes (and .info data, project associations, etc), and + * the current status of the site relative to the currently available releases. + * These two arrays are cached in the {cache_update} table and used whenever + * possible. The cache is cleared whenever the administrator visits the status + * report, available updates report, or the module or theme administration + * pages, since we should always recompute the most current values on any of + * those pages. * * Note: while both of these arrays are expensive to compute (in terms of disk * I/O and some fairly heavy CPU processing), neither of these is the actual @@ -726,13 +765,13 @@ function update_calculate_project_update_status($project, &$project_data, $avail * hour and never get invalidated just by visiting a page on the site. * * @param $cid - * The cache id of data to return from the cache. Valid options are + * The cache ID of data to return from the cache. Valid options are * 'update_project_data' and 'update_project_projects'. * * @return * The cached value of the $projects array generated by - * update_calculate_project_data() or update_get_projects(), or an empty - * array when the cache is cleared. + * update_calculate_project_data() or update_get_projects(), or an empty array + * when the cache is cleared. */ function update_project_cache($cid) { $projects = array(); @@ -764,13 +803,13 @@ function update_project_cache($cid) { } /** - * Filter the project .info data to only save attributes we need. + * Filters the project .info data to only save attributes we need. * * @param array $info * Array of .info file data as returned by drupal_parse_info_file(). * * @return - * Array of .info file data we need for the Update manager. + * Array of .info file data we need for the update manager. * * @see _update_process_info_list() */ diff --git a/modules/update/update.css b/modules/update/update.css index d30dfb6e5..ba45fe6b9 100644 --- a/modules/update/update.css +++ b/modules/update/update.css @@ -1,3 +1,7 @@ +/** + * @file + * Styles used by the Update Manager module. + */ .update .project { font-weight: bold; diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc index ee5d77b16..860a1b975 100644 --- a/modules/update/update.fetch.inc +++ b/modules/update/update.fetch.inc @@ -6,7 +6,11 @@ */ /** - * Callback to manually check the update status without cron. + * Page callback: Checks for updates and displays the update status report. + * + * Manually checks the update status without the use of cron. + * + * @see update_menu() */ function update_manual_status() { _update_refresh(); @@ -25,7 +29,10 @@ function update_manual_status() { } /** - * Process a step in the batch for fetching available update data. + * Batch callback: Processes a step in batch for fetching available update data. + * + * @param $context + * Reference to an array used for Batch API storage. */ function update_fetch_data_batch(&$context) { $queue = DrupalQueue::get('update_fetch_tasks'); @@ -70,12 +77,12 @@ function update_fetch_data_batch(&$context) { } /** - * Batch API callback when all fetch tasks have been completed. + * Batch callback: Performs actions when all fetch tasks have been completed. * * @param $success - * Boolean indicating the success of the batch. + * TRUE if the batch operation was successful; FALSE if there were errors. * @param $results - * Associative array holding the results of the batch, including the key + * An associative array of results from the batch operation, including the key * 'updated' which holds the total number of projects we fetched available * update data for. */ @@ -96,7 +103,7 @@ function update_fetch_data_finished($success, $results) { } /** - * Attempt to drain the queue of tasks for release history data to fetch. + * Attempts to drain the queue of tasks for release history data to fetch. */ function _update_fetch_data() { $queue = DrupalQueue::get('update_fetch_tasks'); @@ -108,13 +115,14 @@ function _update_fetch_data() { } /** - * Process a task to fetch available update data for a single project. + * Processes a task to fetch available update data for a single project. * - * Once the release history XML data is downloaded, it is parsed and saved - * into the {cache_update} table in an entry just for that project. + * Once the release history XML data is downloaded, it is parsed and saved into + * the {cache_update} table in an entry just for that project. * * @param $project * Associative array of information about the project to fetch data for. + * * @return * TRUE if we fetched parsable XML, otherwise FALSE. */ @@ -184,7 +192,7 @@ function _update_process_fetch_task($project) { } /** - * Clear out all the cached available update data and initiate re-fetching. + * Clears out all the cached available update data and initiates re-fetching. */ function _update_refresh() { module_load_include('inc', 'update', 'update.compare'); @@ -211,7 +219,7 @@ function _update_refresh() { } /** - * Add a task to the queue for fetching release history data for a project. + * Adds a task to the queue for fetching release history data for a project. * * We only create a new fetch task if there's no task already in the queue for * this particular project (based on 'fetch_task::' entries in the @@ -219,8 +227,8 @@ function _update_refresh() { * * @param $project * Associative array of information about a project as created by - * update_get_projects(), including keys such as 'name' (short name), - * and the 'info' array with data from a .info file for the project. + * update_get_projects(), including keys such as 'name' (short name), and the + * 'info' array with data from a .info file for the project. * * @see update_get_projects() * @see update_get_available() @@ -260,14 +268,17 @@ function _update_create_fetch_task($project) { /** * Generates the URL to fetch information about project updates. * - * This figures out the right URL to use, based on the project's .info file - * and the global defaults. Appends optional query arguments when the site is + * This figures out the right URL to use, based on the project's .info file and + * the global defaults. Appends optional query arguments when the site is * configured to report usage stats. * * @param $project * The array of project information from update_get_projects(). * @param $site_key - * The anonymous site key hash (optional). + * (optional) The anonymous site key hash. Defaults to an empty string. + * + * @return + * The URL for fetching information about updates to the specified project. * * @see update_fetch_data() * @see _update_process_fetch_task() @@ -293,10 +304,11 @@ function _update_build_fetch_url($project, $site_key = '') { } /** - * Return the base of the URL to fetch available update data for a project. + * Returns the base of the URL to fetch available update data for a project. * * @param $project * The array of project information from update_get_projects(). + * * @return * The base of the URL used for fetching available update data. This does * not include the path elements to specify a particular project, version, @@ -309,10 +321,10 @@ function _update_get_fetch_url_base($project) { } /** - * Perform any notifications that should be done once cron fetches new data. + * Performs any notifications that should be done once cron fetches new data. * - * This method checks the status of the site using the new data and depending - * on the configuration of the site, notifies administrators via email if there + * This method checks the status of the site using the new data and, depending + * on the configuration of the site, notifies administrators via e-mail if there * are new releases or missing security updates. * * @see update_requirements() @@ -340,14 +352,19 @@ function _update_cron_notify() { else { $target_language = $default_language; } - drupal_mail('update', 'status_notify', $target, $target_language, $params); + $message = drupal_mail('update', 'status_notify', $target, $target_language, $params); + // Track when the last mail was successfully sent to avoid sending + // too many e-mails. + if ($message['result']) { + variable_set('update_last_email_notification', REQUEST_TIME); + } } } } } /** - * Parse the XML of the Drupal release history info files. + * Parses the XML of the Drupal release history info files. * * @param $raw_xml * A raw XML string of available release data for a given project. diff --git a/modules/update/update.info b/modules/update/update.info index c60e62255..2068f46cc 100644 --- a/modules/update/update.info +++ b/modules/update/update.info @@ -6,8 +6,8 @@ core = 7.x files[] = update.test configure = admin/reports/updates/settings -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/update/update.install b/modules/update/update.install index 70fb6c328..baf49538e 100644 --- a/modules/update/update.install +++ b/modules/update/update.install @@ -2,26 +2,25 @@ /** * @file - * Install, update and uninstall functions for the update module. + * Install, update, and uninstall functions for the Update Manager module. */ /** * Implements hook_requirements(). * * @return - * An array describing the status of the site regarding available updates. - * If there is no update data, only one record will be returned, indicating - * that the status of core can't be determined. If data is available, there - * will be two records: one for core, and another for all of contrib - * (assuming there are any contributed modules or themes enabled on the - * site). In addition to the fields expected by hook_requirements ('value', - * 'severity', and optionally 'description'), this array will contain a - * 'reason' attribute, which is an integer constant to indicate why the - * given status is being returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or - * UPDATE_UNKNOWN). This is used for generating the appropriate e-mail - * notification messages during update_cron(), and might be useful for other - * modules that invoke update_requirements() to find out if the site is up - * to date or not. + * An array describing the status of the site regarding available updates. If + * there is no update data, only one record will be returned, indicating that + * the status of core can't be determined. If data is available, there will be + * two records: one for core, and another for all of contrib (assuming there + * are any contributed modules or themes enabled on the site). In addition to + * the fields expected by hook_requirements ('value', 'severity', and + * optionally 'description'), this array will contain a 'reason' attribute, + * which is an integer constant to indicate why the given status is being + * returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or UPDATE_UNKNOWN). This + * is used for generating the appropriate e-mail notification messages during + * update_cron(), and might be useful for other modules that invoke + * update_requirements() to find out if the site is up to date or not. * * @see _update_message_text() * @see _update_cron_notify() @@ -83,6 +82,7 @@ function update_uninstall() { 'update_check_frequency', 'update_fetch_url', 'update_last_check', + 'update_last_email_notification', 'update_notification_threshold', 'update_notify_emails', 'update_max_fetch_attempts', @@ -96,19 +96,19 @@ function update_uninstall() { } /** - * Private helper method to fill in the requirements array. + * Fills in the requirements array. * * This is shared for both core and contrib to generate the right elements in * the array for hook_requirements(). * * @param $project - * Array of information about the project we're testing as returned by - * update_calculate_project_data(). + * Array of information about the project we're testing as returned by + * update_calculate_project_data(). * @param $type - * What kind of project is this ('core' or 'contrib'). + * What kind of project this is ('core' or 'contrib'). * * @return - * An array to be included in the nested $requirements array. + * An array to be included in the nested $requirements array. * * @see hook_requirements() * @see update_requirements() @@ -186,5 +186,5 @@ function update_update_7001() { } /** - * @} End of "addtogroup updates-6.x-to-7.x" + * @} End of "addtogroup updates-6.x-to-7.x". */ diff --git a/modules/update/update.manager.inc b/modules/update/update.manager.inc index d9fd86ff2..85b587de2 100644 --- a/modules/update/update.manager.inc +++ b/modules/update/update.manager.inc @@ -2,7 +2,8 @@ /** * @file - * Administrative screens and processing functions for the update manager. + * Administrative screens and processing functions of the Update Manager module. + * * This allows site administrators with the 'administer software updates' * permission to either upgrade existing projects, or download and install new * ones, so long as the killswitch setting ('allow_authorize_operations') is @@ -11,51 +12,52 @@ * To install new code, the administrator is prompted for either the URL of an * archive file, or to directly upload the archive file. The archive is loaded * into a temporary location, extracted, and verified. If everything is - * successful, the user is redirected to authorize.php to type in their file - * transfer credentials and authorize the installation to proceed with - * elevated privileges, such that the extracted files can be copied out of the - * temporary location and into the live web root. + * successful, the user is redirected to authorize.php to type in file transfer + * credentials and authorize the installation to proceed with elevated + * privileges, such that the extracted files can be copied out of the temporary + * location and into the live web root. * * Updating existing code is a more elaborate process. The first step is a - * selection form where the user is presented with a table of installed - * projects that are missing newer releases. The user selects which projects - * they wish to upgrade, and presses the "Download updates" button to - * continue. This sets up a batch to fetch all the selected releases, and - * redirects to admin/update/download to display the batch progress bar as it - * runs. Each batch operation is responsible for downloading a single file, - * extracting the archive, and verifying the contents. If there are any - * errors, the user is redirected back to the first page with the error - * messages. If all downloads were extacted and verified, the user is instead - * redirected to admin/update/ready, a landing page which reminds them to - * backup their database and asks if they want to put the site offline during - * the upgrade. Once the user presses the "Install updates" button, they are - * redirected to authorize.php to supply their web root file access - * credentials. The authorized operation (which lives in update.authorize.inc) - * sets up a batch to copy each extracted update from the temporary location - * into the live web root. + * selection form where the user is presented with a table of installed projects + * that are missing newer releases. The user selects which projects they wish to + * update, and presses the "Download updates" button to continue. This sets up a + * batch to fetch all the selected releases, and redirects to + * admin/update/download to display the batch progress bar as it runs. Each + * batch operation is responsible for downloading a single file, extracting the + * archive, and verifying the contents. If there are any errors, the user is + * redirected back to the first page with the error messages. If all downloads + * were extacted and verified, the user is instead redirected to + * admin/update/ready, a landing page which reminds them to backup their + * database and asks if they want to put the site offline during the update. + * Once the user presses the "Install updates" button, they are redirected to + * authorize.php to supply their web root file access credentials. The + * authorized operation (which lives in update.authorize.inc) sets up a batch to + * copy each extracted update from the temporary location into the live web + * root. */ /** - * @defgroup update_manager_update Update manager: update + * @defgroup update_manager_update Update Manager module: update * @{ - * Update manager for updating existing code. + * Update Manager module functionality for updating existing code. * * Provides a user interface to update existing code. */ /** - * Build the form for the update manager page to update existing projects. + * Form constructor for the update form of the Update Manager module. * * This presents a table with all projects that have available updates with * checkboxes to select which ones to upgrade. * - * @param $form - * @param $form_state * @param $context - * String representing the context from which we're trying to update, can be: - * 'module', 'theme' or 'report'. - * @return - * The form array for selecting which projects to update. + * String representing the context from which we're trying to update. + * Allowed values are 'module', 'theme', and 'report'. + * + * @see update_manager_update_form_validate() + * @see update_manager_update_form_submit() + * @see update_menu() + * @ingroup forms */ function update_manager_update_form($form, $form_state = array(), $context) { if (!_update_manager_check_backends($form, 'update')) { @@ -263,7 +265,7 @@ function update_manager_update_form($form, $form_state = array(), $context) { } /** - * Returns HTML for the first page in the update manager wizard to select projects. + * Returns HTML for the first page in the process of updating projects. * * @param $variables * An associative array containing: @@ -280,7 +282,11 @@ function theme_update_manager_update_form($variables) { } /** - * Validation callback to ensure that at least one project is selected. + * Form validation handler for update_manager_update_form(). + * + * Ensures that at least one project is selected. + * + * @see update_manager_update_form_submit() */ function update_manager_update_form_validate($form, &$form_state) { if (!empty($form_state['values']['projects'])) { @@ -295,11 +301,11 @@ function update_manager_update_form_validate($form, &$form_state) { } /** - * Submit function for the main update form. + * Form submission handler for update_manager_update_form(). * - * This sets up a batch to download, extract and verify the selected releases + * Sets up a batch that downloads, extracts, and verifies the selected releases. * - * @see update_manager_update_form() + * @see update_manager_update_form_validate() */ function update_manager_update_form_submit($form, &$form_state) { $projects = array(); @@ -329,7 +335,12 @@ function update_manager_update_form_submit($form, &$form_state) { } /** - * Batch callback invoked when the download batch is completed. + * Batch callback: Performs actions when the download batch is completed. + * + * @param $success + * TRUE if the batch operation was successful, FALSE if there were errors. + * @param $results + * An associative array of results from the batch operation. */ function update_manager_download_batch_finished($success, $results) { if (!empty($results['errors'])) { @@ -352,15 +363,21 @@ function update_manager_download_batch_finished($success, $results) { } /** + * Form constructor for the update ready form. + * * Build the form when the site is ready to update (after downloading). * * This form is an intermediary step in the automated update workflow. It is - * presented to the site administrator after all the required updates have - * been downloaded and verified. The point of this page is to encourage the - * user to backup their site, gives them the opportunity to put the site - * offline, and then asks them to confirm that the update should continue. - * After this step, the user is redirected to authorize.php to enter their - * file transfer credentials and attempt to complete the update. + * presented to the site administrator after all the required updates have been + * downloaded and verified. The point of this page is to encourage the user to + * backup their site, give them the opportunity to put the site offline, and + * then ask them to confirm that the update should continue. After this step, + * the user is redirected to authorize.php to enter their file transfer + * credentials and attempt to complete the update. + * + * @see update_manager_update_ready_form_submit() + * @see update_menu() + * @ingroup forms */ function update_manager_update_ready_form($form, &$form_state) { if (!_update_manager_check_backends($form, 'update')) { @@ -389,13 +406,13 @@ function update_manager_update_ready_form($form, &$form_state) { } /** - * Submit handler for the form to confirm that an update should continue. + * Form submission handler for update_manager_update_ready_form(). * * 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. + * update, do so now. Otherwise, pull information about all the required 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() @@ -454,26 +471,27 @@ function update_manager_update_ready_form_submit($form, &$form_state) { */ /** - * @defgroup update_manager_install Update manager: install + * @defgroup update_manager_install Update Manager module: install * @{ - * Update manager for installing new code. + * Update Manager module functionality for installing new code. * * Provides a user interface to install new code. */ /** - * Build the form for the update manager page to install new projects. + * Form constructor for the install form of the Update Manager module. * * This presents a place to enter a URL or upload an archive file to use to * install a new module or theme. * - * @param $form - * @param $form_state * @param $context - * String representing the context from which we're trying to install, can - * be: 'module', 'theme' or 'report'. - * @return - * The form array for selecting which project to install. + * String representing the context from which we're trying to install. + * Allowed values are 'module', 'theme', and 'report'. + * + * @see update_manager_install_form_validate() + * @see update_manager_install_form_submit() + * @see update_menu() + * @ingroup forms */ function update_manager_install_form($form, &$form_state, $context) { if (!_update_manager_check_backends($form, 'install')) { @@ -524,11 +542,11 @@ function update_manager_install_form($form, &$form_state, $context) { * @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. + * 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 + * 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. */ @@ -586,7 +604,9 @@ function _update_manager_check_backends(&$form, $operation) { } /** - * Validate the form for installing a new project via the update manager. + * Form validation handler for update_manager_install_form(). + * + * @see update_manager_install_form_submit() */ function update_manager_install_form_validate($form, &$form_state) { if (!($form_state['values']['project_url'] XOR !empty($_FILES['files']['name']['project_upload']))) { @@ -601,7 +621,7 @@ function update_manager_install_form_validate($form, &$form_state) { } /** - * Handle form submission when installing new projects via the update manager. + * Form submission handler for update_manager_install_form(). * * 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 @@ -611,6 +631,7 @@ function update_manager_install_form_validate($form, &$form_state) { * via authorize.php which will copy the extracted files from the temporary * location into the live site. * + * @see update_manager_install_form_validate() * @see update_authorize_run_install() * @see system_authorized_init() * @see system_authorized_get_url() @@ -728,26 +749,26 @@ function update_manager_install_form_submit($form, &$form_state) { */ /** - * @defgroup update_manager_file Update manager: file management + * @defgroup update_manager_file Update Manager module: file management * @{ - * Update manager file management functions. + * Update Manager module file management functions. * - * These functions are used by the update manager to copy, extract - * and verify archive files. + * These functions are used by the update manager to copy, extract, and verify + * archive files. */ /** - * Unpack a downloaded archive file. + * Unpacks a downloaded archive file. * - * @param string $project - * The short name of the project to download. * @param string $file * The filename of the archive you wish to extract. * @param string $directory * The directory you wish to extract the archive into. + * * @return Archiver * The Archiver object used to extract the archive. - * @throws Exception on failure. + * + * @throws Exception */ function update_manager_archive_extract($file, $directory) { $archiver = archiver_get_archiver($file); @@ -775,7 +796,7 @@ function update_manager_archive_extract($file, $directory) { } /** - * Verify an archive after it has been downloaded and extracted. + * Verifies an archive after it has been downloaded and extracted. * * This function is responsible for invoking hook_verify_update_archive(). * @@ -787,18 +808,17 @@ function update_manager_archive_extract($file, $directory) { * The directory that the archive was extracted into. * * @return array - * An array of error messages to display if the archive was invalid. If - * there are no errors, it will be an empty array. - * + * An array of error messages to display if the archive was invalid. If there + * are no errors, it will be an empty array. */ function update_manager_archive_verify($project, $archive_file, $directory) { return module_invoke_all('verify_update_archive', $project, $archive_file, $directory); } /** - * Copies a file from $url to the temporary directory for updates. + * Copies a file from the specified URL to the temporary directory for updates. * - * If the file has already been downloaded, returns the the local path. + * Returns the local path if the file has already been downloaded. * * @param $url * The URL of the file on the server. @@ -827,18 +847,18 @@ function update_manager_file_get($url) { } /** - * Batch operation: download, unpack, and verify a project. + * Batch callback: Downloads, unpacks, and verifies a project. * - * This function assumes that the provided URL points to a file archive of - * some sort. The URL can have any scheme that we have a file stream wrapper - * to support. The file is downloaded to a local cache. + * This function assumes that the provided URL points to a file archive of some + * sort. The URL can have any scheme that we have a file stream wrapper to + * support. The file is downloaded to a local cache. * * @param string $project * The short name of the project to download. * @param string $url * The URL to download a specific project release archive file. * @param array $context - * Reference to an array used for BatchAPI storage. + * Reference to an array used for Batch API storage. * * @see update_manager_download_page() */ @@ -887,8 +907,8 @@ 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 + * 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). * diff --git a/modules/update/update.module b/modules/update/update.module index bf8b06848..85c0968d5 100644 --- a/modules/update/update.module +++ b/modules/update/update.module @@ -2,10 +2,13 @@ /** * @file - * The "Update status" module checks for available updates of Drupal core and - * any installed contributed modules and themes. It warns site administrators - * if newer releases are available via the system status report - * (admin/reports/status), the module and theme pages, and optionally via email. + * Handles updates of Drupal core and contributed projects. + * + * The module checks for available updates of Drupal core and any installed + * contributed modules and themes. It warns site administrators if newer + * releases are available via the system status report (admin/reports/status), + * the module and theme pages, and optionally via e-mail. It also provides the + * ability to install contributed modules and themes via an user interface. */ /** @@ -246,11 +249,14 @@ function update_menu() { } /** - * Determine if the current user can access the updater menu items. + * Access callback: Resolves if the current user can access updater menu items. + * + * It both enforces the 'administer software updates' permission and the global + * kill switch for the authorize.php script. * - * This is used as a menu system access callback. It both enforces the - * 'administer software updates' permission and the global killswitch for the - * authorize.php script. + * @return + * TRUE if the current user can access the updater menu items; FALSE + * otherwise. * * @see update_menu() */ @@ -294,13 +300,18 @@ function update_cron() { // configured notifications about the new status. update_refresh(); update_fetch_data(); - _update_cron_notify(); } else { // Otherwise, see if any individual projects are now stale or still // missing data, and if so, try to fetch the data. update_get_available(TRUE); } + if ((REQUEST_TIME - variable_get('update_last_email_notification', 0)) > $interval) { + // If configured time between notifications elapsed, send email about + // updates possibly available. + module_load_include('inc', 'update', 'update.fetch'); + _update_cron_notify(); + } // Clear garbage from disk. update_clear_update_disk_cache(); @@ -327,10 +338,10 @@ function update_themes_disabled($themes) { } /** - * Implements hook_form_FORM_ID_alter(). + * Implements hook_form_FORM_ID_alter() for system_modules(). * - * Adds a submit handler to the system modules form, so that if a site admin - * saves the form, we invalidate the cache of available updates. + * Adds a form submission handler to the system modules form, so that if a site + * admin saves the form, we invalidate the cache of available updates. * * @see _update_cache_clear() */ @@ -339,7 +350,9 @@ function update_form_system_modules_alter(&$form, $form_state) { } /** - * Helper function for use as a form submit callback. + * Form submission handler for system_modules(). + * + * @see update_form_system_modules_alter() */ function update_cache_clear_submit($form, &$form_state) { // Clear all update module caches. @@ -347,7 +360,7 @@ function update_cache_clear_submit($form, &$form_state) { } /** - * Prints a warning message when there is no data about available updates. + * Returns a warning message when there is no data about available updates. */ function _update_no_data() { $destination = drupal_get_destination(); @@ -358,20 +371,22 @@ function _update_no_data() { } /** - * Internal helper to try to get the update information from the cache - * if possible, and to refresh the cache when necessary. + * Tries to get update information from cache and refreshes it when necessary. * * In addition to checking the cache lifetime, this function also ensures that * there are no .info files for enabled modules or themes that have a newer * modification timestamp than the last time we checked for available update - * data. If any .info file was modified, it almost certainly means a new - * version of something was installed. Without fresh available update data, - * the logic in update_calculate_project_data() will be wrong and produce - * confusing, bogus results. + * data. If any .info file was modified, it almost certainly means a new version + * of something was installed. Without fresh available update data, the logic in + * update_calculate_project_data() will be wrong and produce confusing, bogus + * results. * * @param $refresh - * Boolean to indicate if this method should refresh the cache automatically - * if there's no data. + * (optional) Boolean to indicate if this method should refresh the cache + * automatically if there's no data. Defaults to FALSE. + * + * @return + * Array of data about available releases, keyed by project shortname. * * @see update_refresh() * @see update_get_projects() @@ -428,7 +443,11 @@ function update_get_available($refresh = FALSE) { } /** - * Wrapper to load the include file and then create a new fetch task. + * Creates a new fetch task after loading the necessary include file. + * + * @param $project + * Associative array of information about a project. See update_get_projects() + * for the keys used. * * @see _update_create_fetch_task() */ @@ -438,7 +457,7 @@ function update_create_fetch_task($project) { } /** - * Wrapper to load the include file and then refresh the release data. + * Refreshes the release data after loading the necessary include file. * * @see _update_refresh() */ @@ -448,7 +467,9 @@ function update_refresh() { } /** - * Wrapper to load the include file and then attempt to fetch update data. + * Attempts to fetch update data after loading the necessary include file. + * + * @see _update_fetch_data() */ function update_fetch_data() { module_load_include('inc', 'update', 'update.fetch'); @@ -456,7 +477,7 @@ function update_fetch_data() { } /** - * Return all currently cached data about available releases for all projects. + * Returns all currently cached data about available releases for all projects. * * @return * Array of data about available releases, keyed by project shortname. @@ -481,17 +502,17 @@ function _update_get_cached_available_releases() { /** * Implements hook_mail(). * - * Constructs the email notification message when the site is out of date. + * Constructs the e-mail notification message when the site is out of date. * * @param $key * Unique key to indicate what message to build, always 'status_notify'. * @param $message * Reference to the message array being built. * @param $params - * Array of parameters to indicate what kind of text to include in the - * message body. This is a keyed array of message type ('core' or 'contrib') - * as the keys, and the status reason constant (UPDATE_NOT_SECURE, etc) for - * the values. + * Array of parameters to indicate what kind of text to include in the message + * body. This is a keyed array of message type ('core' or 'contrib') as the + * keys, and the status reason constant (UPDATE_NOT_SECURE, etc) for the + * values. * * @see drupal_mail() * @see _update_cron_notify() @@ -518,22 +539,23 @@ function update_mail($key, &$message, $params) { } /** - * Helper function to return the appropriate message text when the site is out - * of date or missing a security update. + * Returns the appropriate message text when site is out of date or not secure. * * These error messages are shared by both update_requirements() for the * site-wide status report at admin/reports/status and in the body of the - * notification emails generated by update_cron(). + * notification e-mail messages generated by update_cron(). * * @param $msg_type - * String to indicate what kind of message to generate. Can be either - * 'core' or 'contrib'. + * String to indicate what kind of message to generate. Can be either 'core' + * or 'contrib'. * @param $msg_reason * Integer constant specifying why message is generated. * @param $report_link - * Boolean that controls if a link to the updates report should be added. + * (optional) Boolean that controls if a link to the updates report should be + * added. Defaults to FALSE. * @param $language - * An optional language object to use. + * (optional) A language object to use. Defaults to NULL. + * * @return * The properly translated error message for the given key. */ @@ -603,10 +625,9 @@ function _update_message_text($msg_type, $msg_reason, $report_link = FALSE, $lan } /** - * Private sort function to order projects based on their status. + * Orders projects based on their status. * - * @see update_requirements() - * @see uasort() + * Callback for uasort() within update_requirements(). */ function _update_project_status_sort($a, $b) { // The status constants are numerically in the right order, so we can @@ -621,17 +642,16 @@ function _update_project_status_sort($a, $b) { /** * Returns HTML for the last time we checked for update data. * - * In addition to properly formating the given timestamp, this function also + * In addition to properly formatting the given timestamp, this function also * provides a "Check manually" link that refreshes the available update and * redirects back to the same page. * * @param $variables * An associative array containing: - * - 'last': The timestamp when the site last checked for available updates. + * - last: The timestamp when the site last checked for available updates. * * @see theme_update_report() * @see theme_update_available_updates_form() - * * @ingroup themeable */ function theme_update_last_check($variables) { @@ -647,7 +667,7 @@ function theme_update_last_check($variables) { * Implements hook_verify_update_archive(). * * First, we ensure that the archive isn't a copy of Drupal core, which the - * Update manager does not yet support. @see http://drupal.org/node/606592 + * update manager does not yet support. See http://drupal.org/node/606592 * * Then, we make sure that at least one module included in the archive file has * an .info file which claims that the code is compatible with the current @@ -719,19 +739,19 @@ function update_verify_update_archive($project, $archive_file, $directory) { * cleared when we're populating it after successfully fetching new available * update data. Usage of the core cache API results in all sorts of potential * problems that would result in attempting to fetch available update data all - * the time, including if a site has a "minimum cache lifetime" (which is both - * a minimum and a maximum) defined, or if a site uses memcache or another - * plug-able cache system that assumes volatile caches. - * - * Update module still uses the {cache_update} table, but instead of using - * cache_set(), cache_get(), and cache_clear_all(), there are private helper - * functions that implement these same basic tasks but ensure that the cache - * is not prematurely cleared, and that the data is always stored in the + * the time, including if a site has a "minimum cache lifetime" (which is both a + * minimum and a maximum) defined, or if a site uses memcache or another + * pluggable cache system that assumes volatile caches. + * + * The Update Manager module still uses the {cache_update} table, but instead of + * using cache_set(), cache_get(), and cache_clear_all(), there are private + * helper functions that implement these same basic tasks but ensure that the + * cache is not prematurely cleared, and that the data is always stored in the * database, even if memcache or another cache backend is in use. */ /** - * Store data in the private update status cache table. + * Stores data in the private update status cache table. * * @param $cid * The cache ID to save the data with. @@ -743,6 +763,8 @@ function update_verify_update_archive($project, $archive_file, $directory) { * by explicitly using _update_cache_clear(). * - A Unix timestamp: Indicates that the item should be kept at least until * the given time, after which it will be invalidated. + * + * @see _update_cache_get() */ function _update_cache_set($cid, $data, $expire) { $fields = array( @@ -764,12 +786,15 @@ function _update_cache_set($cid, $data, $expire) { } /** - * Retrieve data from the private update status cache table. + * Retrieves data from the private update status cache table. * * @param $cid * The cache ID to retrieve. + * * @return - * The data for the given cache ID, or NULL if the ID was not found. + * An array of data for the given cache ID, or NULL if the ID was not found. + * + * @see _update_cache_set() */ function _update_cache_get($cid) { $cache = db_query("SELECT data, created, expire, serialized FROM {cache_update} WHERE cid = :cid", array(':cid' => $cid))->fetchObject(); @@ -782,7 +807,10 @@ function _update_cache_get($cid) { } /** - * Return an array of cache items with a given cache ID prefix. + * Returns an array of cache items with a given cache ID prefix. + * + * @param string $cid_prefix + * The cache ID prefix. * * @return * Associative array of cache items, keyed by cache ID. @@ -808,12 +836,12 @@ function _update_get_cache_multiple($cid_prefix) { * Invalidates cached data relating to update status. * * @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 except - * fetch tasks. + * (optional) Cache ID of the record to clear from the private update module + * cache. If empty, all records will be cleared from the table except fetch + * tasks. Defaults to NULL. * @param $wildcard - * If $wildcard is TRUE, cache IDs starting with $cid are deleted in - * addition to the exact cache ID specified by $cid. + * (optional) If TRUE, cache IDs starting with $cid are deleted in addition to + * the exact cache ID specified by $cid. Defaults to FALSE. */ function _update_cache_clear($cid = NULL, $wildcard = FALSE) { if (empty($cid)) { @@ -838,18 +866,18 @@ function _update_cache_clear($cid = NULL, $wildcard = FALSE) { /** * Implements hook_flush_caches(). * - * Called from update.php (among others) to flush the caches. - * Since we're running update.php, we are likely to install a new version of - * something, in which case, we want to check for available update data again. - * However, because we have our own caching system, we need to directly clear - * the database table ourselves at this point and return nothing, for example, - * on sites that use memcache where cache_clear_all() won't know how to purge - * this data. + * Called from update.php (among others) to flush the caches. Since we're + * running update.php, we are likely to install a new version of something, in + * which case, we want to check for available update data again. However, + * because we have our own caching system, we need to directly clear the + * database table ourselves at this point and return nothing, for example, on + * sites that use memcache where cache_clear_all() won't know how to purge this + * data. * - * However, we only want to do this from update.php, since otherwise, we'd - * lose all the available update data on every cron run. So, we specifically - * check if the site is in MAINTENANCE_MODE == 'update' (which indicates - * update.php is running, not update module... alas for overloaded names). + * However, we only want to do this from update.php, since otherwise, we'd lose + * all the available update data on every cron run. So, we specifically check if + * the site is in MAINTENANCE_MODE == 'update' (which indicates update.php is + * running, not update module... alas for overloaded names). */ function update_flush_caches() { if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { @@ -863,7 +891,7 @@ function update_flush_caches() { */ /** - * Return a short unique identifier for this Drupal installation. + * Returns a short unique identifier for this Drupal installation. * * @return * An eight character string uniquely identifying this Drupal installation. @@ -877,14 +905,15 @@ function _update_manager_unique_identifier() { } /** - * Return the directory where update archive files should be extracted. + * Returns the directory where update archive files should be extracted. * * @param $create - * If TRUE, attempt to create the directory if it does not already exist. + * (optional) Whether to attempt to create the directory if it does not + * already exist. Defaults to TRUE. * * @return - * The full path to the temporary directory where update file archives - * should be extracted. + * The full path to the temporary directory where update file archives should + * be extracted. */ function _update_manager_extract_directory($create = TRUE) { $directory = &drupal_static(__FUNCTION__, ''); @@ -898,14 +927,15 @@ function _update_manager_extract_directory($create = TRUE) { } /** - * Return the directory where update archive files should be cached. + * Returns the directory where update archive files should be cached. * * @param $create - * If TRUE, attempt to create the directory if it does not already exist. + * (optional) Whether to attempt to create the directory if it does not + * already exist. Defaults to TRUE. * * @return - * The full path to the temporary directory where update file archives - * should be cached. + * The full path to the temporary directory where update file archives should + * be cached. */ function _update_manager_cache_directory($create = TRUE) { $directory = &drupal_static(__FUNCTION__, ''); @@ -919,7 +949,7 @@ function _update_manager_cache_directory($create = TRUE) { } /** - * Clear the temporary files and directories based on file age from disk. + * Clears the temporary files and directories based on file age from disk. */ function update_clear_update_disk_cache() { // List of update module cache directories. Do not create the directories if @@ -936,19 +966,19 @@ function update_clear_update_disk_cache() { } /** - * Delete stale files and directories from the Update manager disk cache. + * Deletes stale files and directories from the update manager disk cache. * - * Files and directories older than 6 hours and development snapshots older - * than 5 minutes are considered stale. We only cache development snapshots - * for 5 minutes since otherwise updated snapshots might not be downloaded as + * Files and directories older than 6 hours and development snapshots older than + * 5 minutes are considered stale. We only cache development snapshots for 5 + * minutes since otherwise updated snapshots might not be downloaded as * expected. * * When checking file ages, we need to use the ctime, not the mtime - * (modification time) since many (all?) tar implementations go out of their - * way to set the mtime on the files they create to the timestamps recorded - * in the tarball. We want to see the last time the file was changed on disk, - * which is left alone by tar and correctly set to the time the archive file - * was unpacked. + * (modification time) since many (all?) tar implementations go out of their way + * to set the mtime on the files they create to the timestamps recorded in the + * tarball. We want to see the last time the file was changed on disk, which is + * left alone by tar and correctly set to the time the archive file was + * unpacked. * * @param $path * A string containing a file path or (streamwrapper) URI. diff --git a/modules/update/update.report.inc b/modules/update/update.report.inc index 3f5933acb..f256575fa 100644 --- a/modules/update/update.report.inc +++ b/modules/update/update.report.inc @@ -6,7 +6,9 @@ */ /** - * Menu callback. Generate a page about the update status of projects. + * Page callback: Generates a page about the update status of projects. + * + * @see update_menu() */ function update_status() { if ($available = update_get_available(TRUE)) { @@ -257,6 +259,7 @@ function theme_update_report($variables) { * - status: The integer code for a project's current update status. * * @see update_calculate_project_data() + * @ingroup themeable */ function theme_update_status_label($variables) { switch ($variables['status']) { diff --git a/modules/update/update.settings.inc b/modules/update/update.settings.inc index 60ac3ca8e..5cd241498 100644 --- a/modules/update/update.settings.inc +++ b/modules/update/update.settings.inc @@ -6,7 +6,11 @@ */ /** - * Form builder for the update settings tab. + * Form constructor for the update settings form. + * + * @see update_settings_validate() + * @see update_settings_submit() + * @ingroup forms */ function update_settings($form) { $form['update_check_frequency'] = array( @@ -57,9 +61,11 @@ function update_settings($form) { } /** - * Validation callback for the settings form. + * Form validation handler for update_settings(). + * + * Validates the e-mail addresses and ensures the field is formatted correctly. * - * Validates the email addresses and ensures the field is formatted correctly. + * @see update_settings_submit() */ function update_settings_validate($form, &$form_state) { if (!empty($form_state['values']['update_notify_emails'])) { @@ -89,13 +95,15 @@ function update_settings_validate($form, &$form_state) { } /** - * Submit handler for the settings tab. + * Form submission handler for update_settings(). + * + * Also invalidates the cache of available updates if the "Check for updates of + * disabled modules and themes" setting is being changed. The available updates + * report needs to refetch available update data after this setting changes or + * it would show misleading things (e.g., listing the disabled projects on the + * site with the "No available releases found" warning). * - * Also invalidates the cache of available updates if the "Check for updates - * of disabled modules and themes" setting is being changed. The available - * updates report need to refetch available update data after this setting - * changes or it would show misleading things (e.g. listing the disabled - * projects on the site with the "No available releases found" warning). + * @see update_settings_validate() */ function update_settings_submit($form, $form_state) { $op = $form_state['values']['op']; diff --git a/modules/update/update.test b/modules/update/update.test index 8daa82155..e297194ae 100644 --- a/modules/update/update.test +++ b/modules/update/update.test @@ -2,38 +2,43 @@ /** * @file - * Tests for update.module. + * This file contains tests for the Update Manager module. * - * This file contains tests for the update module. The overarching methodology - * of these tests is we need to compare a given state of installed modules and - * themes (e.g. version, project grouping, timestamps, etc) vs. a current - * state of what the release history XML files we fetch say is available. We - * have dummy XML files (in the 'tests' subdirectory) that describe various - * scenarios of what's available for different test projects, and we have - * dummy .info file data (specified via hook_system_info_alter() in the - * update_test helper module) describing what's currently installed. Each - * test case defines a set of projects to install, their current state (via - * the 'update_test_system_info' variable) and the desired available update - * data (via the 'update_test_xml_map' variable), and then performs a series - * of assertions that the report matches our expectations given the specific - * initial state and availability scenario. + * The overarching methodology of these tests is we need to compare a given + * state of installed modules and themes (e.g., version, project grouping, + * timestamps, etc) against a current state of what the release history XML + * files we fetch say is available. We have dummy XML files (in the + * modules/update/tests directory) that describe various scenarios of what's + * available for different test projects, and we have dummy .info file data + * (specified via hook_system_info_alter() in the update_test helper module) + * describing what's currently installed. Each test case defines a set of + * projects to install, their current state (via the 'update_test_system_info' + * variable) and the desired available update data (via the + * 'update_test_xml_map' variable), and then performs a series of assertions + * that the report matches our expectations given the specific initial state and + * availability scenario. */ /** - * Base class to define some shared functions used by all update tests. + * Defines some shared functions used by all update tests. */ class UpdateTestHelper extends DrupalWebTestCase { + /** - * Refresh the update status based on the desired available update scenario. + * Refreshes the update status based on the desired available update scenario. * * @param $xml_map - * Array that maps project names to availability scenarios to fetch. - * The key '#all' is used if a project-specific mapping is not defined. + * Array that maps project names to availability scenarios to fetch. The key + * '#all' is used if a project-specific mapping is not defined. + * @param $url + * (optional) A string containing the URL to fetch update data from. + * Defaults to 'update-test'. * * @see update_test_mock_page() */ protected function refreshUpdateStatus($xml_map, $url = 'update-test') { - // Tell update module to fetch from the URL provided by update_test module. + // Tell the Update Manager module to fetch from the URL provided by + // update_test module. variable_set('update_fetch_url', url($url, array('absolute' => TRUE))); // Save the map for update_test_mock_page() to use. variable_set('update_test_xml_map', $xml_map); @@ -42,7 +47,7 @@ class UpdateTestHelper extends DrupalWebTestCase { } /** - * Run a series of assertions that are applicable for all update statuses. + * Runs a series of assertions that are applicable to all update statuses. */ protected function standardTests() { $this->assertRaw('<h3>' . t('Drupal core') . '</h3>'); @@ -52,12 +57,15 @@ class UpdateTestHelper extends DrupalWebTestCase { } +/** + * Tests behavior related to discovering and listing updates to Drupal core. + */ class UpdateCoreTestCase extends UpdateTestHelper { public static function getInfo() { return array( 'name' => 'Update core functionality', - 'description' => 'Tests the update module through a series of functional tests using mock XML data.', + 'description' => 'Tests the Update Manager module through a series of functional tests using mock XML data.', 'group' => 'Update', ); } @@ -69,7 +77,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Tests the update module when no updates are available. + * Tests the Update Manager module when no updates are available. */ function testNoUpdatesAvailable() { $this->setSystemInfo7_0(); @@ -81,7 +89,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Tests the update module when one normal update ("7.1") is available. + * Tests the Update Manager module when one normal update is available. */ function testNormalUpdateAvailable() { $this->setSystemInfo7_0(); @@ -96,7 +104,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Tests the update module when a security update ("7.2") is available. + * Tests the Update Manager module when a security update is available. */ function testSecurityUpdateAvailable() { $this->setSystemInfo7_0(); @@ -111,7 +119,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Ensure proper results where there are date mismatches among modules. + * Ensures proper results where there are date mismatches among modules. */ function testDatestampMismatch() { $system_info = array( @@ -134,7 +142,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Check that running cron updates the list of available updates. + * Checks that running cron updates the list of available updates. */ function testModulePageRunCron() { $this->setSystemInfo7_0(); @@ -147,7 +155,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Check the messages at admin/modules when the site is up to date. + * Checks the messages at admin/modules when the site is up to date. */ function testModulePageUpToDate() { $this->setSystemInfo7_0(); @@ -164,7 +172,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Check the messages at admin/modules when missing an update. + * Checks the messages at admin/modules when an update is missing. */ function testModulePageRegularUpdate() { $this->setSystemInfo7_0(); @@ -181,7 +189,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Check the messages at admin/modules when missing a security update. + * Checks the messages at admin/modules when a security update is missing. */ function testModulePageSecurityUpdate() { $this->setSystemInfo7_0(); @@ -216,7 +224,7 @@ class UpdateCoreTestCase extends UpdateTestHelper { } /** - * Tests the update module when the update server returns 503 (Service unavailable) errors. + * Tests the Update Manager module when the update server returns 503 errors. */ function testServiceUnavailable() { $this->refreshUpdateStatus(array(), '503-error'); @@ -252,6 +260,9 @@ class UpdateCoreTestCase extends UpdateTestHelper { $this->assertEqual($queue->numberOfItems(), 2, 'Queue contains two items'); } + /** + * Sets the version to 7.0 when no project-specific mapping is defined. + */ protected function setSystemInfo7_0() { $setting = array( '#all' => array( @@ -263,12 +274,15 @@ class UpdateCoreTestCase extends UpdateTestHelper { } +/** + * Tests behavior related to handling updates to contributed modules and themes. + */ class UpdateTestContribCase extends UpdateTestHelper { public static function getInfo() { return array( 'name' => 'Update contrib functionality', - 'description' => 'Tests how the update module handles contributed modules and themes in a series of functional tests using mock XML data.', + 'description' => 'Tests how the Update Manager module handles contributed modules and themes in a series of functional tests using mock XML data.', 'group' => 'Update', ); } @@ -308,7 +322,7 @@ class UpdateTestContribCase extends UpdateTestHelper { } /** - * Test the basic functionality of a contrib module on the status report. + * Tests the basic functionality of a contrib module on the status report. */ function testUpdateContribBasic() { $system_info = array( @@ -336,17 +350,17 @@ class UpdateTestContribCase extends UpdateTestHelper { } /** - * Test that contrib projects are ordered by project name. + * Tests that contrib projects are ordered by project name. * * If a project contains multiple modules, we want to make sure that the - * available updates report is sorted by the parent project names, not by - * the names of the modules included in each project. In this test case, we - * have 2 contrib projects, "BBB Update test" and "CCC Update test". - * However, we have a module called "aaa_update_test" that's part of the - * "CCC Update test" project. We need to make sure that we see the "BBB" - * project before the "CCC" project, even though "CCC" includes a module - * that's processed first if you sort alphabetically by module name (which - * is the order we see things inside system_rebuild_module_data() for example). + * available updates report is sorted by the parent project names, not by the + * names of the modules included in each project. In this test case, we have + * two contrib projects, "BBB Update test" and "CCC Update test". However, we + * have a module called "aaa_update_test" that's part of the "CCC Update test" + * project. We need to make sure that we see the "BBB" project before the + * "CCC" project, even though "CCC" includes a module that's processed first + * if you sort alphabetically by module name (which is the order we see things + * inside system_rebuild_module_data() for example). */ function testUpdateContribOrder() { // We want core to be version 7.0. @@ -408,7 +422,7 @@ class UpdateTestContribCase extends UpdateTestHelper { } /** - * Test that subthemes are notified about security updates for base themes. + * Tests that subthemes are notified about security updates for base themes. */ function testUpdateBaseThemeSecurityUpdate() { // Only enable the subtheme, not the base theme. @@ -449,7 +463,7 @@ class UpdateTestContribCase extends UpdateTestHelper { } /** - * Test that disabled themes are only shown when desired. + * Tests that disabled themes are only shown when desired. */ function testUpdateShowDisabledThemes() { // Make sure all the update_test_* themes are disabled. @@ -510,7 +524,7 @@ class UpdateTestContribCase extends UpdateTestHelper { } /** - * Make sure that if we fetch from a broken URL, sane things happen. + * Makes sure that if we fetch from a broken URL, sane things happen. */ function testUpdateBrokenFetchURL() { $system_info = array( @@ -566,13 +580,12 @@ class UpdateTestContribCase extends UpdateTestHelper { } /** - * Check that hook_update_status_alter() works to change a status. + * Checks that hook_update_status_alter() works to change a status. * * We provide the same external data as if aaa_update_test 7.x-1.0 were * installed and that was the latest release. Then we use * hook_update_status_alter() to try to mark this as missing a security - * update, then assert if we see the appropriate warnings on the right - * pages. + * update, then assert if we see the appropriate warnings on the right pages. */ function testHookUpdateStatusAlter() { variable_set('allow_authorize_operations', TRUE); @@ -628,11 +641,15 @@ class UpdateTestContribCase extends UpdateTestHelper { } +/** + * Tests project upload and extract functionality. + */ class UpdateTestUploadCase extends UpdateTestHelper { + public static function getInfo() { return array( 'name' => 'Upload and extract module functionality', - 'description' => 'Tests the update module\'s upload and extraction functionality.', + 'description' => 'Tests the Update Manager module\'s upload and extraction functionality.', 'group' => 'Update', ); } @@ -673,7 +690,7 @@ class UpdateTestUploadCase extends UpdateTestHelper { } /** - * Ensure that archiver extensions are properly merged in the UI. + * Ensures that archiver extensions are properly merged in the UI. */ function testFileNameExtensionMerging() { $this->drupalGet('admin/modules/install'); @@ -684,7 +701,7 @@ class UpdateTestUploadCase extends UpdateTestHelper { } /** - * Check the messages on Update manager pages when missing a security update. + * Checks the messages on update manager pages when missing a security update. */ function testUpdateManagerCoreSecurityUpdateMessages() { $setting = array( @@ -725,6 +742,9 @@ class UpdateTestUploadCase extends UpdateTestHelper { } +/** + * Tests update functionality unrelated to the database. + */ class UpdateCoreUnitTestCase extends DrupalUnitTestCase { public static function getInfo() { @@ -741,7 +761,7 @@ class UpdateCoreUnitTestCase extends DrupalUnitTestCase { } /** - * Tests _update_build_fetch_url according to issue 1481156 + * Tests that _update_build_fetch_url() builds the URL correctly. */ function testUpdateBuildFetchUrl() { //first test that we didn't break the trivial case diff --git a/modules/user/tests/user_form_test.info b/modules/user/tests/user_form_test.info index 215368355..5db169310 100644 --- a/modules/user/tests/user_form_test.info +++ b/modules/user/tests/user_form_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/user/user.info b/modules/user/user.info index e1f0df04f..f17ef35e8 100644 --- a/modules/user/user.info +++ b/modules/user/user.info @@ -9,8 +9,8 @@ required = TRUE configure = admin/config/people stylesheets[all][] = user.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/modules/user/user.install b/modules/user/user.install index e46f29d8e..217577de7 100644 --- a/modules/user/user.install +++ b/modules/user/user.install @@ -830,6 +830,15 @@ function user_update_7015() { )); } +/** + * @} End of "addtogroup updates-6.x-to-7.x". + */ + +/** + * @addtogroup updates-7.x-extra + * @{ + */ + /** * Update the database to match the schema. */ @@ -890,15 +899,6 @@ function user_update_7017() { return $message; } -/** - * @} End of "addtogroup updates-6.x-to-7.x" - */ - -/** - * @addtogroup updates-7.x-extra - * @{ - */ - /** * Ensure there is an index on {users}.picture. */ @@ -909,5 +909,5 @@ function user_update_7018() { } /** - * @} End of "addtogroup updates-7.x-extra" + * @} End of "addtogroup updates-7.x-extra". */ diff --git a/modules/user/user.module b/modules/user/user.module index 94ecaa2df..47ac64273 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -159,6 +159,10 @@ function user_entity_info() { 'uri callback' => 'user_uri', 'label callback' => 'format_username', 'fieldable' => TRUE, + // $user->language is only the preferred user language for the user + // interface textual elements. As it is not necessarily related to the + // language assigned to fields, we do not define it as the entity language + // key. 'entity keys' => array( 'id' => 'uid', ), @@ -819,7 +823,12 @@ function user_access($string, $account = NULL) { /** * Checks for usernames blocked by user administration. * - * @return boolean TRUE for blocked users, FALSE for active. + * @param $name + * A string containing a name of the user. + * + * @return + * Object with property 'name' (the user name), if the user is blocked; + * FALSE if the user is not blocked. */ function user_is_blocked($name) { return db_select('users') @@ -3707,7 +3716,8 @@ function user_register_form($form, &$form_state) { // Attach field widgets, and hide the ones where the 'user_register_form' // setting is not on. - field_attach_form('user', $form['#user'], $form, $form_state); + $langcode = entity_language('user', $form['#user']); + field_attach_form('user', $form['#user'], $form, $form_state, $langcode); foreach (field_info_instances('user', 'user') as $field_name => $instance) { if (empty($instance['settings']['user_register_form'])) { $form[$field_name]['#access'] = FALSE; diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 25f4528f0..c4b68b9f6 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -113,8 +113,9 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a drupal_goto(); } else { - // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds. - $timeout = 86400; + // Time out, in seconds, until login URL expires. Defaults to 24 hours = + // 86400 seconds. + $timeout = variable_get('user_password_reset_timeout', 86400); $current = REQUEST_TIME; // Some redundant checks for extra security ? $users = user_load_multiple(array($uid), array('status' => '1')); @@ -262,7 +263,8 @@ function user_profile_form($form, &$form_state, $account, $category = 'account') if ($category == 'account') { user_account_form($form, $form_state); // Attach field widgets. - field_attach_form('user', $account, $form, $form_state); + $langcode = entity_language('user', $account); + field_attach_form('user', $account, $form, $form_state, $langcode); } $form['actions'] = array('#type' => 'actions'); diff --git a/modules/user/user.test b/modules/user/user.test index 2efe5b070..b53db0769 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -451,6 +451,58 @@ class UserLoginTestCase extends DrupalWebTestCase { } } +/** + * Tests resetting a user password. + */ +class UserPasswordResetTestCase extends DrupalWebTestCase { + protected $profile = 'standard'; + + public static function getInfo() { + return array( + 'name' => 'Reset password', + 'description' => 'Ensure that password reset methods work as expected.', + 'group' => 'User', + ); + } + + /** + * Tests password reset functionality. + */ + function testUserPasswordReset() { + // Create a user. + $account = $this->drupalCreateUser(); + $this->drupalLogin($account); + $this->drupalLogout(); + // Attempt to reset password. + $edit = array('name' => $account->name); + $this->drupalPost('user/password', $edit, t('E-mail new password')); + // Confirm the password reset. + $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.'); + } + + /** + * Attempts login using an expired password reset link. + */ + function testUserPasswordResetExpired() { + // Set password reset timeout variable to 43200 seconds = 12 hours. + $timeout = 43200; + variable_set('user_password_reset_timeout', $timeout); + + // Create a user. + $account = $this->drupalCreateUser(); + $this->drupalLogin($account); + // Load real user object. + $account = user_load($account->uid, TRUE); + $this->drupalLogout(); + + // To attempt an expired password reset, create a password reset link as if + // its request time was 60 seconds older than the allowed limit of timeout. + $bogus_timestamp = REQUEST_TIME - variable_get('user_password_reset_timeout', 86400) - 60; + $this->drupalGet("user/reset/$account->uid/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login)); + $this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.'); + } +} + /** * Test cancelling a user. */ @@ -2119,9 +2171,13 @@ class UserRolesAssignmentTestCase extends DrupalWebTestCase { /** * Check role on user object. * - * @param object $account User. - * @param integer $rid Role id. - * @param bool $is_assigned True if the role should present on the account. + * @param object $account + * The user account to check. + * @param string $rid + * The role ID to search for. + * @param bool $is_assigned + * (optional) Whether to assert that $rid exists (TRUE) or not (FALSE). + * Defaults to TRUE. */ private function userLoadAndCheckRoleAssigned($account, $rid, $is_assigned = TRUE) { $account = user_load($account->uid, TRUE); diff --git a/profiles/minimal/minimal.info b/profiles/minimal/minimal.info index 1fd9bddd0..5a9d4c322 100644 --- a/profiles/minimal/minimal.info +++ b/profiles/minimal/minimal.info @@ -5,8 +5,8 @@ core = 7.x dependencies[] = block dependencies[] = dblog -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/profiles/minimal/minimal.install b/profiles/minimal/minimal.install index 58a0f1277..6099da1ce 100644 --- a/profiles/minimal/minimal.install +++ b/profiles/minimal/minimal.install @@ -1,7 +1,7 @@ <?php /** * @file - * Install, update and uninstall functions for the the minimal install profile. + * Install, update and uninstall functions for the minimal install profile. */ /** diff --git a/profiles/standard/standard.info b/profiles/standard/standard.info index 4a1efd44c..5e36db4ed 100644 --- a/profiles/standard/standard.info +++ b/profiles/standard/standard.info @@ -24,8 +24,8 @@ dependencies[] = field_ui dependencies[] = file dependencies[] = rdf -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info index ac7935ba2..94a9186b6 100644 --- a/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info +++ b/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE files[] = drupal_system_listing_compatible_test.test -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info index df5eb4f3c..28ff2b847 100644 --- a/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info +++ b/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info @@ -8,8 +8,8 @@ version = VERSION core = 6.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/profiles/testing/testing.info b/profiles/testing/testing.info index 4920d5708..4791a0d79 100644 --- a/profiles/testing/testing.info +++ b/profiles/testing/testing.info @@ -4,8 +4,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 87eaabbc5..30699a0a2 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -262,7 +262,7 @@ $drupal_hash_salt = ''; * To see what PHP settings are possible, including whether they can be set at * runtime (by using ini_set()), read the PHP documentation: * http://www.php.net/manual/en/ini.list.php - * See drupal_initialize_variables() in includes/bootstrap.inc for required + * See drupal_environment_initialize() in includes/bootstrap.inc for required * runtime settings and the .htaccess file for non-runtime settings. Settings * defined there should not be duplicated here so as to avoid conflict issues. */ diff --git a/themes/bartik/bartik.info b/themes/bartik/bartik.info index f4aed308c..7492f2959 100644 --- a/themes/bartik/bartik.info +++ b/themes/bartik/bartik.info @@ -34,8 +34,8 @@ regions[footer] = Footer settings[shortcut_module_link] = 0 -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/themes/bartik/css/style.css b/themes/bartik/css/style.css index 4fb821025..8e9520210 100644 --- a/themes/bartik/css/style.css +++ b/themes/bartik/css/style.css @@ -1488,7 +1488,7 @@ ol.search-results { } .search-results li:last-child { border-bottom: none; - padding-bottom: none; + padding-bottom: 0; margin-bottom: 1em; } .search-results .search-snippet-info { diff --git a/themes/bartik/template.php b/themes/bartik/template.php index bdad570d1..7466e05ce 100644 --- a/themes/bartik/template.php +++ b/themes/bartik/template.php @@ -150,7 +150,7 @@ function bartik_field__taxonomy_term_reference($variables) { $output .= '</ul>'; // Render the top-level DIV. - $output = '<div class="' . $variables['classes'] . (!in_array('clearfix', $variables['classes_array']) ? ' clearfix' : '') . '">' . $output . '</div>'; + $output = '<div class="' . $variables['classes'] . (!in_array('clearfix', $variables['classes_array']) ? ' clearfix' : '') . '"' . $variables['attributes'] .'>' . $output . '</div>'; return $output; } diff --git a/themes/garland/garland.info b/themes/garland/garland.info index 7dfbf5302..0b8016c7f 100644 --- a/themes/garland/garland.info +++ b/themes/garland/garland.info @@ -7,8 +7,8 @@ stylesheets[all][] = style.css stylesheets[print][] = print.css settings[garland_width] = fluid -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/themes/seven/seven.info b/themes/seven/seven.info index a59f0f274..c51d58ec3 100644 --- a/themes/seven/seven.info +++ b/themes/seven/seven.info @@ -13,8 +13,8 @@ regions[page_bottom] = Page bottom regions[sidebar_first] = First sidebar regions_hidden[] = sidebar_first -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" diff --git a/themes/stark/stark.info b/themes/stark/stark.info index fd7aa87e1..0d35fc3c6 100644 --- a/themes/stark/stark.info +++ b/themes/stark/stark.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x stylesheets[all][] = layout.css -; Information added by drupal.org packaging script on 2012-05-02 -version = "7.14" +; Information added by drupal.org packaging script on 2012-08-01 +version = "7.15" project = "drupal" -datestamp = "1335997555" +datestamp = "1343839327" -- GitLab