diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b29a58a..039ab82 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,395 @@ -Drupal 7.20, 2013-02-20 - test +Drupal 7.36, 2015-04-01 +----------------------- +- Added a 'file_public_schema' variable which allows modules that define + publicly-accessible streams in hook_stream_wrappers() to bypass file download + access checks when processing managed file upload fields. +- Fixed a bug that caused database query tags not to be added to search-related + database queries under many circumstances, and which prevented the + corresponding hook_query_TAG_alter() implementations from being called. +- Fixed the "for" attribute on managed file upload field labels to improve + accessibility (minor markup change). +- Added a 'javascript_always_use_jquery' variable which can be set to FALSE by + sites that may not need jQuery loaded on all pages, and a 'requires_jquery' + option to drupal_add_js() which modules can set to FALSE when adding + JavaScript files that have no dependency on jQuery (API addition: + https://www.drupal.org/node/2462717). +- Fixed incorrect foreign keys in the User module's role_permission and + users_roles database tables. +- Changed permission descriptions throughout Drupal core to consistently link + to relevant administrative pages, regardless of whether the user viewing the + Permissions page can view the page being linked to (minor UI change). +- Fixed the drupal_add_region_content() function so that it actually adds + content to the page. +- Added an 'image_suppress_itok_output' variable to allow sites already using + the existing 'image_allow_insecure_derivatives' variable to also prevent + security tokens from appearing in image derivative URLs. +- Fixed double-escaping of theme names in the Block module administrative + interface (minor string change). +- Added basic support for Xdebug when running automated tests. +- Fixed a bug which caused previewing a node to remove elements from the node + being edited. With this fix, calling node_preview() will no longer modify the + passed-in node object (minor API change). +- Added a user_has_role() function to check whether a user has a particular + role (API addition: https://www.drupal.org/node/2462411). +- Fixed installation failures when an opcode cache is enabled. +- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused private + files to be inaccessible. +- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused user + pictures to be lost. +- Fixed missing language code in hook_field_attach_view_alter() when it is + invoked from field_view_field(). +- Stopped sending ETag and Last-Modified headers for uncached page requests, + since they break caching for certain Varnish and Nginx configurations. +- Changed the Simpletest module to allow PSR-4 test classes to be used in + Drupal 7. +- Fixed a fatal error that occurred when using the Comment module's "Unpublish + comment containing keyword(s)" action. +- Changed the "lang" attribute on language links to "xml:lang" so it validates + as XHTML (minor markup change). +- Prevented the form API from allowing arrays to be submitted for various form + elements, such as textfields, textareas, and password fields (API change: + https://www.drupal.org/node/2462723). +- Fixed a bug in the Contact module which caused the global user object to have + the incorrect name and e-mail address during the remainder of the page + request after the contact form is submitted. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.35, 2015-03-18 +---------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-001. + +Drupal 7.34, 2014-11-19 +---------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-006. + +Drupal 7.33, 2014-11-07 +----------------------- +- Began storing the file modification time of each module and theme in the + {system} database table so that contributed modules can use it to identify + recently changed modules and themes (minor data structure change to the + return value of system_get_info() and other related functions). +- Added a "Did you mean?" feature to the run-tests.sh script for running + automated tests from the command line, to help developers who are attempting + to run a particular test class or group. +- Changed the date format used in various HTTP headers output by Drupal core + from RFC 1123 format to RFC 7231 format. +- Added a "block_cache_bypass_node_grants" variable to allow sites which have + node access modules enabled to use the block cache if desired (API addition). +- Made image derivative generation HTTP requests return a 404 error (rather + than a 500 error) when the source image does not exist. +- Fixed a bug which caused user pictures to be removed from the user object + after saving, and resulted in data loss if the user account was subsequently + re-saved. +- Fixed a bug in which field_has_data() did not return TRUE for fields that + only had data in older entity revisions, leading to loss of the field's data + when the field configuration was edited. +- Fixed a bug which caused the Ajax progress throbber to appear misaligned in + many situatons (minor styling change). +- Prevented the Bartik theme from lower-casing the "Permalink" link on + comments, for improved multilingual support (minor UI change). +- Added a "preferred_menu_links" tag to the database query that is used by + menu_link_get_preferred() to find the preferred menu link for a given path, + to make it easier to alter. +- Increased the maximum allowed length of block titles to 255 characters + (database schema change to the {block} table). +- Removed the Field module's field_modules_uninstalled() function, since it did + not do anything when it was invoked. +- Added a "theme_hook_original" variable to templates and theme functions and + an optional sitewide theme debug mode, to provide contextual information in + the page's HTML to theme developers. The theme debug mode is based on the one + used with Twig in Drupal 8 and can be accessed by setting the "theme_debug" + variable to TRUE (API addition). +- Added an entity_view_mode_prepare() API function to allow entity-defining + modules to properly invoke hook_entity_view_mode_alter(), and used it + throughout Drupal core to fix bugs with the invocation of that hook (API + change: https://www.drupal.org/node/2369141). +- Security improvement: Made the database API's orderBy() method sanitize the + sort direction ("ASC" or "DESC") for queries built with db_select(), so that + calling code does not have to. +- Changed the RDF module to consistently output RDF metadata for nodes and + comments near where the node is rendered in the HTML (minor markup and data + structure change). +- Added an HTML class to RDFa metatags throughout Drupal to prevent them from + accidentally affecting the site appearance (minor markup change). +- Fixed a bug in the Unicode requirements check which prevented installing + Drupal on PHP 5.6. +- Fixed a bug which caused drupal_get_bootstrap_phase() to abort the bootstrap + when called early in the page request. +- Renamed the "Search result" view mode to "Search result highlighting input" + to better reflect how it is used (UI change). +- Improved database queries generated by EntityFieldQuery in the case where + delta or language condition groups are used, to reduce the number of INNER + JOINs (this is a minor data structure change affecting code which implements + hook_query_alter() on these queries). +- Removed special-case behavior for file uploads which allowed user #1 to + bypass maximum file size and user quota limits. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.32, 2014-10-15 +---------------------- +- Fixed security issues (SQL injection). See SA-CORE-2014-005. + +Drupal 7.31, 2014-08-06 +---------------------- +- Fixed security issues (denial of service). See SA-CORE-2014-004. + +Drupal 7.30, 2014-07-24 +----------------------- +- Fixed a regression introduced in Drupal 7.29 that caused files or images + attached to taxonomy terms to be deleted when the taxonomy term was edited + and resaved (and other related bugs with contributed and custom modules). +- Added a warning on the permissions page to recommend restricting access to + the "View site reports" permission to trusted administrators. See + DRUPAL-PSA-2014-002. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.29, 2014-07-16 +---------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003. + +Drupal 7.28, 2014-05-08 +----------------------- +- Fixed a regression introduced in Drupal 7.27 that caused JavaScript to break + on older browsers (such as Internet Explorer 8 and earlier) when Ajax was + used. +- Increased the timeout used by the Update Manager module when it fetches data + from drupal.org (from 5 seconds to 30 seconds), to work around a problem + which causes incomplete information about security updates to be presented to + site administrators. This fix may lead to a performance slowdown on the + Update Manager administration pages, when installing Drupal distributions, + and (for sites that use the automated cron feature) on occasional page loads + by site visitors. +- Fixed the behavior of the token system's "[node:summary]" token when the body + field does not have a manual summary. +- Changed the behavior of db_query_temporary() so that it works on SELECT + queries even when they have leading comments/whitespace. A side effect of + this fix is that db_query_temporary() will now fail with an error if it is + ever used on non-SELECT queries. +- Added a "node_admin_filter" tag to the database query used to build the list + of nodes on the content administration page, to make it easier to alter. +- Made the cron queue system log any exceptions that are thrown while an item + in the queue is being processed, rather than stopping the entire PHP request. +- Improved screen reader support by adding an aria-live HTML attribute to file + upload fields when there is an error uploading the file (minor markup + change). +- Made the pager on the Tracker module listing pages show the same number of + items as other pagers throughout Drupal core (minor UI change). +- Fixed a bug which caused caches not to be properly cleared when a file entity + was saved or deleted. +- Added several missing countries to the default list returned by + country_get_list() (string change). +- Replaced the term "weight" with "influence" in the content ranking settings + for search, and added help text for administrators (string change). +- Fixed untranslatable text strings in the administrative interface for the + "Crop" effect provided by the Image module (minor string change). +- Fixed a bug in the Taxonomy module update function introduced in Drupal 7.26 + that caused memory and CPU problems on sites with very large numbers of + unpublished nodes. +- Numerous small bug fixes. +- Numerous API documentation improvements. +- Additional automated test coverage. + +Drupal 7.27, 2014-04-16 +---------------------- +- Fixed security issues (information disclosure). See SA-CORE-2014-002. + +Drupal 7.26, 2014-01-15 +---------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001. + +Drupal 7.25, 2014-01-02 +----------------------- +- Fixed a bug in node_save() which prevented the saved node from being updated + in hook_node_insert() and other similar hooks. +- Added a meta tag to install.php to prevent it from being indexed by search + engines even when Drupal is installed in a subfolder (minor markup change). +- Fixed a bug in the database API that caused frequent deadlock errors when + running merge queries on some servers. +- Performance improvement: Prevented block rehashing from writing blocks to the + database on every cache clear and cron run when the blocks have not changed. + This fix results in an extra 'saved' key which is added and set to TRUE for + each block returned by _block_rehash() that actually is saved to the database + (data structure change). +- Added an optional 'skip on cron' parameter to hook_cron_queue_info() to allow + queues to avoid being automatically processed on cron runs (API addition). +- Fixed a bug which caused hook_block_view_MODULE_DELTA_alter() to never be + invoked if the block delta had a hyphen in it. To implement the hook when the + block delta has a hyphen, modules should now replace hyphens with underscores + when constructing the function name for the hook implementation. +- Fixed a bug which caused cached pages to sometimes be sent to the browser + with incorrect compression. The fix adds a new 'page_compressed' key to the + $cache->data array returned by drupal_page_get_cache() (minor data structure + change). +- Fixed broken tests on PHP 5.5. +- Made the File and Image modules more robust when saving entities that have + deleted files attached. The code in file_field_presave() will now remove the + record of the deleted file from the entity before saving (minor data + structure change). +- Standardized menu callback functions throughout Drupal core to return + MENU_NOT_FOUND and MENU_ACCESS_DENIED rather than printing their own "page + not found" or "access denied" pages (minor API change in the return value of + these functions under some circumstances). +- Fixed a bug in which caches were not properly cleared when a node was deleted + via the administrative interface. +- Changed the Bartik theme to render content contained in
, and
+ similar tags in a larger font size, so it is easier to read.
+- Fixed a bug in the Search module that caused exceptions to be thrown during
+ searches if the server was not configured to represent decimal points as a
+ period.
+- Fixed a regression in the Image module that made image_style_url() not work
+ when a relative path (rather than a complete file URI) was passed to it.
+- Added an optional feature to the Statistics module to allow node views to be
+ tracked by Ajax requests rather than during the server-side generation of the
+ page. This allows the node counter to work on sites that use external page
+ caches (string change and new administrative option:
+ https://drupal.org/node/2164069).
+- Added a link to the drupal.org documentation page for cron to the Cron
+ settings page (string change).
+- Added a 'drupal_anonymous_user_object' variable to allow the anonymous user
+ object returned by drupal_anonymous_user() to be overridden with a classed
+ object (API addition).
+- Changed the database API to allow inserts based on a SELECT * query to work
+ correctly.
+- Changed the database schema of the {file_managed} table to allow Drupal to
+ manage files larger than 4 GB.
+- Changed the File module's hook_field_load() implementation to prevent file
+ entity properties which have the same name as file or image field properties
+ from overwriting the field properties (minor API change).
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.24, 2013-11-20
+----------------------
+- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
+
+Drupal 7.23, 2013-08-07
+-----------------------
+- Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module
+ from Drupal 6 to Drupal 7.
+- Fixed the default ordering of CSS files for sites using right-to-left
+ languages, to consistently place the right-to-left override file immediately
+ after the CSS it is overriding (API change: https://drupal.org/node/2058463).
+- Added a drupal_check_memory_limit() API function to allow the memory limit to
+ be checked consistently (API addition).
+- Changed the default web.config file for IIS servers to allow favicon.ico
+ files which are present in the filesystem to be accessed.
+- Fixed inconsistent support for the 'tel' protocol in Drupal's URL filtering
+ functions.
+- Performance improvement: Allowed all hooks to be included in the
+ module_implements() cache, even those that are only invoked on HTTP POST
+ requests.
+- Made the database system replace truncate queries with delete queries when
+ inside a transaction, to fix issues with PostgreSQL and other databases.
+- Fixed a bug which caused nested contextual links to display improperly.
+- Fixed a bug which prevented cached image derivatives from being flushed for
+ private files and other non-default file schemes.
+- Fixed drupal_render() to always return an empty string when there is no
+ output, rather than sometimes returning NULL (minor API change).
+- Added protection to cache_clear_all() to ensure that non-cache tables cannot
+ be truncated (API addition: a new isValidBin() method has been added to the
+ default database cache implementation).
+- Changed the default .htaccess file to support HTTP authorization in CGI
+ environments.
+- Changed the password reset form to pre-fill the username when requested via a
+ URL query parameter, and used this in the error message that appears after a
+ failed login attempt (minor data structure and behavior change).
+- Fixed broken support for foreign keys in the field API.
+- Fixed "No active batch" error when a user cancels their own account.
+- Added a description to the "access content overview" permission on the
+ permissions page (string change).
+- Added a drupal_array_diff_assoc_recursive() function to allow associative
+ arrays to be compared recursively (API addition).
+- Added human-readable labels to image styles, in addition to the existing
+ machine-readable name (API change: https://drupal.org/node/2058503).
+- Moved the drupal_get_hash_salt() function to bootstrap.inc and used it in
+ additional places in the code, for added security in the case where there is
+ no hash salt in settings.php.
+- Fixed a regression in Drupal 7.22 that caused internal server errors for
+ sites running on very old Apache 1.x web servers.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.22, 2013-04-03
+-----------------------
+- Allowed the drupal_http_request() function to be overridden so that
+ additional HTTP request capabilities can be added by contributed modules.
+- Changed the Simpletest module to allow PSR-0 test classes to be used in
+ Drupal 7.
+- Removed an unnecessary "Content-Disposition" header from private file
+ downloads; it prevented many private files from being viewed inline in a web
+ browser.
+- Changed various field API functions to allow them to optionally act on a
+ single field within an entity (API addition: http://drupal.org/node/1825844).
+- Fixed a bug which prevented Drupal's file transfer functionality from working
+ on some PHP 5.4 systems.
+- Fixed incorrect log message when theme() is called for a theme hook that does
+ not exist (minor string change).
+- Fixed Drupal's token-replacement system to allow spaces in the token value.
+- Changed the default behavior after a user creates a node they do not have
+ access to view. The user will now be redirected to the front page rather than
+ an access denied page.
+- Fixed a bug which prevented empty HTTP headers (such as "0") from being set.
+ (Minor behavior change: Callers of drupal_add_http_header() must now set
+ FALSE explicitly to prevent a header from being sent at all; this was already
+ indicated in the function's documentation.)
+- Fixed OpenID errors when more than one module implements hook_openid(). The
+ behavior is now changed so that if more than one module tries to set the same
+ parameter, the last module's change takes effect.
+- Fixed a serious documentation bug: The $name variable in the
+ taxonomy-term.tpl.php theme template was incorrectly documented as being
+ sanitized when in fact it is not.
+- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
+ duplicate permission names in the User module's database tables.
+- Added an empty "datatype" attribute to taxonomy term and username links to
+ make the RDFa markup upward compatible with RDFa 1.1 (minor markup addition).
+- Fixed a bug which caused the denial-of-service protection added in Drupal
+ 7.20 to break certain valid image URLs that had an extra slash in them.
+- Fixed a bug with update queries in the SQLite database driver that prevented
+ Drupal from being installed with SQLite on PHP 5.4.
+- Fixed enforced dependencies errors updating to recent versions of Drupal 7 on
+ certain non-MySQL databases.
+- Refactored the Field module's caching behavior to obtain large improvements
+ in memory usage for sites with many fields and instances (API addition:
+ http://drupal.org/node/1915646).
+- Fixed entity argument not being passed to implementations of
+ hook_file_download_access_alter(). The fix adds an additional context
+ parameter that can be passed when calling drupal_alter() for any hook (API
+ change: http://drupal.org/node/1882722).
+- Fixed broken support for translatable comment fields (API change:
+ http://drupal.org/node/1874724).
+- Added an assertThemeOutput() method to Simpletest to allow tests to check
+ that themed output matches an expected HTML string (API addition).
+- Added a link to "Install another module" after a module has been successfully
+ downloaded via the Update Manager (UI change).
+- Added an optional "exclusive" flag to installation profile .info files which
+ allows Drupal distributions to force a profile to be selected during
+ installation (API addition: http://drupal.org/node/1961012).
+- Fixed a bug which caused the database API to not properly close database
+ connections.
+- Added a link to the URL for running cron from outside the site to the Cron
+ settings page (UI change).
+- Fixed a bug which prevented image styles from being reverted on PHP 5.4.
+- Made the default .htaccess rules protocol sensitive to improve security for
+ sites which use HTTPS and redirect between "www" and non-"www" versions of
+ the page.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.21, 2013-03-06
+-----------------------
+- Allowed sites using the 'image_allow_insecure_derivatives' variable to still
+ have partial protection from the security issues fixed in Drupal 7.20.
+
+Drupal 7.20, 2013-02-20
-----------------------
- Fixed security issues (denial of service). See SA-CORE-2013-002.
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index a2a6511..dc8a855 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -1,4 +1,4 @@
-All Drupal code is Copyright 2001 - 2012 by the original authors.
+All Drupal code is Copyright 2001 - 2013 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
diff --git a/INSTALL.mysql.txt b/INSTALL.mysql.txt
index bee5811..95a8734 100644
--- a/INSTALL.mysql.txt
+++ b/INSTALL.mysql.txt
@@ -20,18 +20,21 @@ initial database files. Next you must log in and set the access database rights:
Again, you will be asked for the 'username' database password. At the MySQL
prompt, enter the following command:
- GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
- ON databasename.*
+ GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
+ CREATE TEMPORARY TABLES ON databasename.*
TO 'username'@'localhost' IDENTIFIED BY 'password';
-where
+where:
'databasename' is the name of your database
- 'username@localhost' is the username of your MySQL account
+ 'username' is the username of your MySQL account
+ 'localhost' is the web server host where Drupal is installed
'password' is the password required for that username
-Note: Unless your database user has all of the privileges listed above, you will
-not be able to run Drupal.
+Note: Unless the database user/host combination for your Drupal installation
+has all of the privileges listed above (except possibly CREATE TEMPORARY TABLES,
+which is currently only used by Drupal core automated tests and some
+contributed modules), you will not be able to install or run Drupal.
If successful, MySQL will reply with:
diff --git a/INSTALL.txt b/INSTALL.txt
index c3a26ad..6f02c05 100644
--- a/INSTALL.txt
+++ b/INSTALL.txt
@@ -20,6 +20,8 @@ Drupal requires:
- MySQL 5.0.15 (or greater) (http://www.mysql.com/).
- MariaDB 5.1.44 (or greater) (http://mariadb.org/). MariaDB is a fully
compatible drop-in replacement for MySQL.
+ - Percona Server 5.1.70 (or greater) (http://www.percona.com/). Percona
+ Server is a backwards-compatible replacement for MySQL.
- PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
- SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
diff --git a/LICENSE.txt b/LICENSE.txt
index 94fb846..d159169 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,12 +1,12 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
- Preamble
+ Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -56,7 +56,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
- GNU GENERAL PUBLIC LICENSE
+ GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
- NO WARRANTY
+ NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
- END OF TERMS AND CONDITIONS
+ END OF TERMS AND CONDITIONS
- How to Apply These Terms to Your New Programs
+ How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt
index 6171566..f5cf6f8 100644
--- a/MAINTAINERS.txt
+++ b/MAINTAINERS.txt
@@ -27,7 +27,6 @@ Ajax system
- Earl Miles 'merlinofchaos' http://drupal.org/user/26979
Base system
-- Károly Négyesi 'chx' http://drupal.org/user/9446
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
@@ -39,7 +38,6 @@ Cache system
- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
Cron system
-- Károly Négyesi 'chx' http://drupal.org/user/9446
- Derek Wright 'dww' http://drupal.org/user/46549
Database system
@@ -55,10 +53,8 @@ Database system
- Sqlite driver
- Damien Tournoud 'DamZ' http://drupal.org/user/22211
- - Károly Négyesi 'chx' http://drupal.org/user/9446
Database update system
-- Károly Négyesi 'chx' http://drupal.org/user/9446
- Ashok Modi 'BTMash' http://drupal.org/user/60422
Entity system
@@ -71,7 +67,6 @@ File system
- Aaron Winborn 'aaron' http://drupal.org/user/33420
Form system
-- Károly Négyesi 'chx' http://drupal.org/user/9446
- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
- Wolfgang Ziegler 'fago' http://drupal.org/user/16747
- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
@@ -105,7 +100,6 @@ Markup
Menu system
- Peter Wolanin 'pwolanin' http://drupal.org/user/49851
-- Károly Négyesi 'chx' http://drupal.org/user/9446
Path system
- Dave Reid 'davereid' http://drupal.org/user/53892
@@ -139,9 +133,6 @@ Accessibility
Documentation
- Jennifer Hodgdon 'jhodgdon' http://drupal.org/user/155601
-Security
-- Greg Knaddison 'greggles' http://drupal.org/user/36762
-
Translations
- Gerhard Killesreiter 'killes' http://drupal.org/user/83
@@ -154,6 +145,20 @@ Node Access
- Ken Rickard 'agentrickard' http://drupal.org/user/20975
- Jess Myrbo 'xjm' http://drupal.org/user/65776
+
+Security team
+-----------------
+
+To report a security issue, see: https://drupal.org/security-team/report-issue
+
+The Drupal security team provides Security Advisories for vulnerabilities,
+assists developers in resolving security issues, and provides security
+documentation. See http://drupal.org/security-team for more information. The
+security team lead is:
+
+- Michael Hess 'mlhess' https://drupal.org/user/102818
+
+
Module maintainers
------------------
@@ -250,7 +255,6 @@ Shortcut module
Simpletest module
- Jimmy Berry 'boombatower' http://drupal.org/user/214218
-- Károly Négyesi 'chx' http://drupal.org/user/9446
Statistics module
- Tim Millwood 'timmillwood' http://drupal.org/user/227849
diff --git a/README.txt b/README.txt
index f4c2f64..60d3da5 100644
--- a/README.txt
+++ b/README.txt
@@ -71,12 +71,12 @@ profiles/your_site_profile/themes respectively to restrict their usage to only
sites that were installed with that specific profile.
More about installation profiles and distributions:
-* Read about the difference between installation profiles and distributions:
- http://drupal.org/node/1089736
-* Download contributed installation profiles and distributions:
- http://drupal.org/project/distributions
-* Develop your own installation profile or distribution:
- http://drupal.org/developing/distributions
+ * Read about the difference between installation profiles and distributions:
+ http://drupal.org/node/1089736
+ * Download contributed installation profiles and distributions:
+ http://drupal.org/project/distributions
+ * Develop your own installation profile or distribution:
+ http://drupal.org/developing/distributions
APPEARANCE
----------
diff --git a/authorize.php b/authorize.php
index d14fa6e..3ea2b20 100644
--- a/authorize.php
+++ b/authorize.php
@@ -4,16 +4,16 @@
* @file
* Administrative script for running authorized file operations.
*
- * Using this script, the site owner (the user actually owning the files on
- * the webserver) can authorize certain file-related operations to proceed
- * with elevated privileges, for example to deploy and upgrade modules or
- * themes. Users should not visit this page directly, but instead use an
- * administrative user interface which knows how to redirect the user to this
- * script as part of a multistep process. This script actually performs the
- * selected operations without loading all of Drupal, to be able to more
- * gracefully recover from errors. Access to the script is controlled by a
- * global killswitch in settings.php ('allow_authorize_operations') and via
- * the 'administer software updates' permission.
+ * Using this script, the site owner (the user actually owning the files on the
+ * webserver) can authorize certain file-related operations to proceed with
+ * elevated privileges, for example to deploy and upgrade modules or themes.
+ * Users should not visit this page directly, but instead use an administrative
+ * user interface which knows how to redirect the user to this script as part of
+ * a multistep process. This script actually performs the selected operations
+ * without loading all of Drupal, to be able to more gracefully recover from
+ * errors. Access to the script is controlled by a global killswitch in
+ * settings.php ('allow_authorize_operations') and via the 'administer software
+ * updates' permission.
*
* There are helper functions for setting up an operation to run via this
* system in modules/system/system.module. For more information, see:
@@ -21,16 +21,17 @@
*/
/**
- * Root directory of Drupal installation.
+ * Defines the root directory of the Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
/**
- * Global flag to identify update.php and authorize.php runs, and so
- * avoid various unwanted operations, such as hook_init() and
- * hook_exit() invokes, css/js preprocessing and translation, and
- * solve some theming issues. This flag is checked on several places
- * in Drupal code (not just authorize.php).
+ * Global flag to identify update.php and authorize.php runs.
+ *
+ * Identifies update.php and authorize.php runs, avoiding unwanted operations
+ * such as hook_init() and hook_exit() invokes, css/js preprocessing and
+ * translation, and solves some theming issues. The flag is checked in other
+ * places in Drupal code (not just authorize.php).
*/
define('MAINTENANCE_MODE', 'update');
@@ -51,7 +52,7 @@ function authorize_access_denied_page() {
* have access to the 'administer software updates' permission.
*
* @return
- * TRUE if the current user can run authorize.php, otherwise FALSE.
+ * TRUE if the current user can run authorize.php, and FALSE if not.
*/
function authorize_access_allowed() {
return variable_get('allow_authorize_operations', TRUE) && user_access('administer software updates');
diff --git a/includes/ajax.inc b/includes/ajax.inc
index 4107029..6e8e277 100644
--- a/includes/ajax.inc
+++ b/includes/ajax.inc
@@ -211,7 +211,7 @@
*
* When returning an Ajax command array, it is often useful to have
* status messages rendered along with other tasks in the command array.
- * In that case the the Ajax commands array may be constructed like this:
+ * In that case the Ajax commands array may be constructed like this:
* @code
* $commands = array();
* $commands[] = ajax_command_replace(NULL, $output);
@@ -251,8 +251,8 @@ function ajax_render($commands = array()) {
// reliably diffed with array_diff_key(), since the number can change
// due to factors unrelated to the inline content, so for now, we strip
// the inline items from Ajax responses, and can add support for them
- // when drupal_add_css() and drupal_add_js() are changed to using md5()
- // or some other hash of the inline content.
+ // when drupal_add_css() and drupal_add_js() are changed to use a hash
+ // of the inline content as the array key.
foreach ($items[$type] as $key => $item) {
if (is_numeric($key)) {
unset($items[$type][$key]);
@@ -276,7 +276,7 @@ function ajax_render($commands = array()) {
$extra_commands = array();
if (!empty($styles)) {
- $extra_commands[] = ajax_command_prepend('head', $styles);
+ $extra_commands[] = ajax_command_add_css($styles);
}
if (!empty($scripts_header)) {
$extra_commands[] = ajax_command_prepend('head', $scripts_header);
@@ -292,7 +292,7 @@ function ajax_render($commands = array()) {
$scripts = drupal_add_js();
if (!empty($scripts['settings'])) {
$settings = $scripts['settings'];
- array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
+ array_unshift($commands, ajax_command_settings(drupal_array_merge_deep_array($settings['data']), TRUE));
}
// Allow modules to alter any Ajax response.
@@ -308,10 +308,11 @@ function ajax_render($commands = array()) {
* pulls the form info from $_POST.
*
* @return
- * An array containing the $form and $form_state. Use the list() function
- * to break these apart:
+ * An array containing the $form, $form_state, $form_id, $form_build_id and an
+ * initial list of Ajax $commands. Use the list() function to break these
+ * apart:
* @code
- * list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
+ * list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
* @endcode
*/
function ajax_get_form() {
@@ -331,6 +332,17 @@ function ajax_get_form() {
drupal_exit();
}
+ // When a page level cache is enabled, the form-build id might have been
+ // replaced from within form_get_cache. If this is the case, it is also
+ // necessary to update it in the browser by issuing an appropriate Ajax
+ // command.
+ $commands = array();
+ if (isset($form['#build_id_old']) && $form['#build_id_old'] != $form['#build_id']) {
+ // If the form build ID has changed, issue an Ajax command to update it.
+ $commands[] = ajax_command_update_build_id($form);
+ $form_build_id = $form['#build_id'];
+ }
+
// Since some of the submit handlers are run, redirects need to be disabled.
$form_state['no_redirect'] = TRUE;
@@ -345,7 +357,7 @@ function ajax_get_form() {
$form_state['input'] = $_POST;
$form_id = $form['#form_id'];
- return array($form, $form_state, $form_id, $form_build_id);
+ return array($form, $form_state, $form_id, $form_build_id, $commands);
}
/**
@@ -366,7 +378,7 @@ function ajax_get_form() {
* @see system_menu()
*/
function ajax_form_callback() {
- list($form, $form_state) = ajax_get_form();
+ list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
drupal_process_form($form['#form_id'], $form, $form_state);
// We need to return the part of the form (or some other content) that needs
@@ -379,7 +391,19 @@ function ajax_form_callback() {
$callback = $form_state['triggering_element']['#ajax']['callback'];
}
if (!empty($callback) && function_exists($callback)) {
- return $callback($form, $form_state);
+ $result = $callback($form, $form_state);
+
+ if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
+ // Turn the response into a #type=ajax array if it isn't one already.
+ $result = array(
+ '#type' => 'ajax',
+ '#commands' => ajax_prepare_response($result),
+ );
+ }
+
+ $result['#commands'] = array_merge($commands, $result['#commands']);
+
+ return $result;
}
}
@@ -836,7 +860,8 @@ function ajax_command_insert($selector, $html, $settings = NULL) {
* @return
* An array suitable for use with the ajax_render() function.
*
- * See @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
+ * See
+ * @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
*/
function ajax_command_replace($selector, $html, $settings = NULL) {
return array(
@@ -1209,3 +1234,49 @@ function ajax_command_restripe($selector) {
'selector' => $selector,
);
}
+
+/**
+ * Creates a Drupal Ajax 'update_build_id' command.
+ *
+ * This command updates the value of a hidden form_build_id input element on a
+ * form. It requires the form passed in to have keys for both the old build ID
+ * in #build_id_old and the new build ID in #build_id.
+ *
+ * The primary use case for this Ajax command is to serve a new build ID to a
+ * form served from the cache to an anonymous user, preventing one anonymous
+ * user from accessing the form state of another anonymous users on Ajax enabled
+ * forms.
+ *
+ * @param $form
+ * The form array representing the form whose build ID should be updated.
+ */
+function ajax_command_update_build_id($form) {
+ return array(
+ 'command' => 'updateBuildId',
+ 'old' => $form['#build_id_old'],
+ 'new' => $form['#build_id'],
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'add_css' command.
+ *
+ * This method will add css via ajax in a cross-browser compatible way.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.add_css()
+ * defined in misc/ajax.js.
+ *
+ * @param $styles
+ * A string that contains the styles to be added.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see misc/ajax.js
+ */
+function ajax_command_add_css($styles) {
+ return array(
+ 'command' => 'add_css',
+ 'data' => $styles,
+ );
+}
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 2cfdfe9..b9d49de 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -8,7 +8,7 @@
/**
* The current system version.
*/
-define('VERSION', '7.20');
+define('VERSION', '7.36');
/**
* Core API compatibility.
@@ -218,12 +218,16 @@ define('LANGUAGE_RTL', 1);
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
/**
- * Flag for drupal_set_title(); text is not sanitized, so run check_plain().
+ * Flag used to indicate that text is not sanitized, so run check_plain().
+ *
+ * @see drupal_set_title()
*/
define('CHECK_PLAIN', 0);
/**
- * Flag for drupal_set_title(); text has already been sanitized.
+ * Flag used to indicate that text has already been sanitized.
+ *
+ * @see drupal_set_title()
*/
define('PASS_THROUGH', -1);
@@ -240,10 +244,19 @@ define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
/**
* Regular expression to match PHP function names.
*
- * @see http://php.net/manual/en/language.functions.php
+ * @see http://php.net/manual/language.functions.php
*/
define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
+/**
+ * A RFC7231 Compliant date.
+ *
+ * http://tools.ietf.org/html/rfc7231#section-7.1.1.1
+ *
+ * Example: Sun, 06 Nov 1994 08:49:37 GMT
+ */
+define('DATE_RFC7231', 'D, d M Y H:i:s \G\M\T');
+
/**
* Provides a caching wrapper to be used in place of large array structures.
*
@@ -274,7 +287,7 @@ define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
* error, and $var will be populated with the contents of $object['foo'], but
* that data will be passed by value, not reference. For more information on
* the PHP limitation, see the note in the official PHP documentation at·
- * http://php.net/manual/en/arrayaccess.offsetget.php on
+ * http://php.net/manual/arrayaccess.offsetget.php on
* ArrayAccess::offsetGet().
*
* By default, the class accounts for caches where calling functions might
@@ -380,11 +393,11 @@ abstract class DrupalCacheArray implements ArrayAccess {
* without necessarily writing back to the persistent cache at the end.
*
* @param $offset
- * The array offset that was request.
+ * The array offset that was requested.
* @param $persist
* Optional boolean to specify whether the offset should be persisted or
* not, defaults to TRUE. When called with $persist = FALSE the offset will
- * be unflagged so that it will not written at the end of the request.
+ * be unflagged so that it will not be written at the end of the request.
*/
protected function persist($offset, $persist = TRUE) {
$this->keysToPersist[$offset] = $persist;
@@ -516,9 +529,8 @@ function timer_stop($name) {
* Returns the appropriate configuration directory.
*
* Returns the configuration path based on the site's hostname, port, and
- * pathname. Uses find_conf_path() to find the current configuration directory.
- * See default.settings.php for examples on how the URL is converted to a
- * directory.
+ * pathname. See default.settings.php for examples on how the URL is converted
+ * to a directory.
*
* @param bool $require_settings
* Only configuration directories with an existing settings.php file
@@ -679,7 +691,8 @@ function drupal_environment_initialize() {
ini_set('session.use_only_cookies', '1');
ini_set('session.use_trans_sid', '0');
// Don't send HTTP headers using PHP's session handler.
- ini_set('session.cache_limiter', 'none');
+ // An empty string is used here to disable the cache limiter.
+ ini_set('session.cache_limiter', '');
// Use httponly session cookies.
ini_set('session.cookie_httponly', '1');
@@ -695,7 +708,14 @@ function drupal_environment_initialize() {
* TRUE if only containing valid characters, or FALSE otherwise.
*/
function drupal_valid_http_host($host) {
- return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
+ // Limit the length of the host name to 1000 bytes to prevent DoS attacks with
+ // long host names.
+ return strlen($host) <= 1000
+ // Limit the number of subdomains and port separators to prevent DoS attacks
+ // in conf_path().
+ && substr_count($host, '.') <= 100
+ && substr_count($host, ':') <= 100
+ && preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
}
/**
@@ -716,7 +736,6 @@ function drupal_settings_initialize() {
if (isset($base_url)) {
// Parse fixed base URL from settings.php.
$parts = parse_url($base_url);
- $http_protocol = $parts['scheme'];
if (!isset($parts['path'])) {
$parts['path'] = '';
}
@@ -792,7 +811,7 @@ function drupal_settings_initialize() {
*
* This function plays a key role in allowing Drupal's resources (modules
* and themes) to be located in different places depending on a site's
- * configuration. For example, a module 'foo' may legally be be located
+ * configuration. For example, a module 'foo' may legally be located
* in any of these three places:
*
* modules/foo/foo.module
@@ -803,7 +822,7 @@ function drupal_settings_initialize() {
* the above, depending on where the module is located.
*
* @param $type
- * The type of the item (i.e. theme, theme_engine, module, profile).
+ * The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
* @param $filename
@@ -811,7 +830,7 @@ function drupal_settings_initialize() {
* than by consulting the database.
*
* @return
- * The filename of the requested item.
+ * The filename of the requested item or NULL if the item is not found.
*/
function drupal_get_filename($type, $name, $filename = NULL) {
// The location of files will not change during the request, so do not use
@@ -841,7 +860,7 @@ function drupal_get_filename($type, $name, $filename = NULL) {
try {
if (function_exists('db_query')) {
$file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
- if (file_exists(DRUPAL_ROOT . '/' . $file)) {
+ if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
$files[$type][$name] = $file;
}
}
@@ -1186,10 +1205,11 @@ function _drupal_set_preferred_header_name($name = NULL) {
* Headers are set in drupal_add_http_header(). Default headers are not set
* if they have been replaced or unset using drupal_add_http_header().
*
- * @param $default_headers
- * An array of headers as name/value pairs.
- * @param $single
- * If TRUE and headers have already be sent, send only the specified header.
+ * @param array $default_headers
+ * (optional) An array of headers as name/value pairs.
+ * @param bool $only_default
+ * (optional) If TRUE and headers have already been sent, send only the
+ * specified headers.
*/
function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
$headers_sent = &drupal_static(__FUNCTION__, FALSE);
@@ -1212,7 +1232,7 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
}
// Skip headers that have been unset.
- elseif ($value) {
+ elseif ($value !== FALSE) {
header($header_names[$name_lower] . ': ' . $value);
}
}
@@ -1225,23 +1245,10 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
* fresh page on every request. This prevents authenticated users from seeing
* locally cached pages.
*
- * Also give each page a unique ETag. This will force clients to include both
- * an If-Modified-Since header and an If-None-Match header when doing
- * conditional requests for the page (required by RFC 2616, section 13.3.4),
- * making the validation more robust. This is a workaround for a bug in Mozilla
- * Firefox that is triggered when Drupal's caching is enabled and the user
- * accesses Drupal via an HTTP proxy (see
- * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
- * user requests a page, and then logs out and requests the same page again,
- * Firefox may send a conditional request based on the page that was cached
- * locally when the user was logged in. If this page did not have an ETag
- * header, the request only contains an If-Modified-Since header. The date will
- * be recent, because with authenticated users the Last-Modified header always
- * refers to the time of the request. If the user accesses Drupal via a proxy
- * server, and the proxy already has a cached copy of the anonymous page with an
- * older Last-Modified date, the proxy may respond with 304 Not Modified, making
- * the client think that the anonymous and authenticated pageviews are
- * identical.
+ * ETag and Last-Modified headers are not set per default for authenticated
+ * users so that browsers do not send If-Modified-Since headers from
+ * authenticated user pages. drupal_serve_page_from_cache() will set appropriate
+ * ETag and Last-Modified headers for cached pages.
*
* @see drupal_page_set_cache()
*/
@@ -1254,9 +1261,7 @@ function drupal_page_header() {
$default_headers = array(
'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
- 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
- 'ETag' => '"' . REQUEST_TIME . '"',
);
drupal_send_headers($default_headers);
}
@@ -1274,7 +1279,7 @@ function drupal_page_header() {
*/
function drupal_serve_page_from_cache(stdClass $cache) {
// Negotiate whether to use compression.
- $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+ $page_compression = !empty($cache->data['page_compressed']);
$return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
// Get headers set in hook_boot(). Keys are lower-case.
@@ -1324,7 +1329,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
drupal_add_http_header($name, $value);
}
- $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
+ $default_headers['Last-Modified'] = gmdate(DATE_RFC7231, $cache->created);
// HTTP/1.0 proxies does not support the Vary header, so prevent any caching
// by sending an Expires date in the past. HTTP/1.1 clients ignores the
@@ -1405,6 +1410,7 @@ function drupal_unpack($obj, $field = 'data') {
* more information, including recommendations on how to break up or not
* break up strings for translation.
*
+ * @section sec_translating_vars Translating Variables
* You should never use t() to translate variables, such as calling
* @code t($text); @endcode, unless the text that the variable holds has been
* passed through t() elsewhere (e.g., $text is one of several translated
@@ -1420,9 +1426,11 @@ function drupal_unpack($obj, $field = 'data') {
* Basically, you can put variables like @name into your string, and t() will
* substitute their sanitized values at translation time. (See the
* Localization API pages referenced above and the documentation of
- * format_string() for details.) Translators can then rearrange the string as
- * necessary for the language (e.g., in Spanish, it might be "blog de @name").
+ * format_string() for details about how to define variables in your string.)
+ * Translators can then rearrange the string as necessary for the language
+ * (e.g., in Spanish, it might be "blog de @name").
*
+ * @section sec_alt_funcs_install Use During Installation Phase
* During the Drupal installation phase, some resources used by t() wil not be
* available to code that needs localization. See st() and get_t() for
* alternatives.
@@ -1484,21 +1492,34 @@ function t($string, array $args = array(), array $options = array()) {
}
/**
- * Replaces placeholders with sanitized values in a string.
+ * Formats a string for HTML display by replacing variable placeholders.
+ *
+ * This function replaces variable placeholders in a string with the requested
+ * values and escapes the values so they can be safely displayed as HTML. It
+ * should be used on any unknown text that is intended to be printed to an HTML
+ * page (especially text that may have come from untrusted users, since in that
+ * case it prevents cross-site scripting and other security problems).
+ *
+ * In most cases, you should use t() rather than calling this function
+ * directly, since it will translate the text (on non-English-only sites) in
+ * addition to formatting it.
*
* @param $string
* A string containing placeholders.
* @param $args
* An associative array of replacements to make. Occurrences in $string of
- * any key in $args are replaced with the corresponding value, after
- * sanitization. The sanitization function depends on the first character of
- * the key:
- * - !variable: Inserted as is. Use this for text that has already been
- * sanitized.
- * - @variable: Escaped to HTML using check_plain(). Use this for anything
- * displayed on a page on the site.
- * - %variable: Escaped as a placeholder for user-submitted content using
- * drupal_placeholder(), which shows up as emphasized text.
+ * any key in $args are replaced with the corresponding value, after optional
+ * sanitization and formatting. The type of sanitization and formatting
+ * depends on the first character of the key:
+ * - @variable: Escaped to HTML using check_plain(). Use this as the default
+ * choice for anything displayed on a page on the site.
+ * - %variable: Escaped to HTML and formatted using drupal_placeholder(),
+ * which makes it display as emphasized text.
+ * - !variable: Inserted as is, with no sanitization or formatting. Only use
+ * this for text that has already been prepared for HTML display (for
+ * example, user-supplied text that has already been run through
+ * check_plain() previously, or is expected to contain some limited HTML
+ * tags and has already been run through filter_xss() previously).
*
* @see t()
* @ingroup sanitization
@@ -1531,12 +1552,13 @@ function format_string($string, array $args = array()) {
* Also validates strings as UTF-8 to prevent cross site scripting attacks on
* Internet Explorer 6.
*
- * @param $text
+ * @param string $text
* The text to be checked or processed.
*
- * @return
- * An HTML safe version of $text, or an empty string if $text is not
- * valid UTF-8.
+ * @return string
+ * An HTML safe version of $text. If $text is not valid UTF-8, an empty string
+ * is returned and, on PHP < 5.4, a warning may be issued depending on server
+ * configuration (see @link https://bugs.php.net/bug.php?id=47494 @endlink).
*
* @see drupal_validate_utf8()
* @ingroup sanitization
@@ -1621,14 +1643,14 @@ function request_uri() {
* information about the passed-in exception is used.
* @param $variables
* Array of variables to replace in the message on display. Defaults to the
- * return value of drupal_decode_exception().
+ * return value of _drupal_decode_exception().
* @param $severity
* The severity of the message, as per RFC 3164.
* @param $link
* A link to associate with the message.
*
* @see watchdog()
- * @see drupal_decode_exception()
+ * @see _drupal_decode_exception()
*/
function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {
@@ -1912,6 +1934,33 @@ function drupal_block_denied($ip) {
}
}
+/**
+ * Returns a URL-safe, base64 encoded string of highly randomized bytes (over the full 8-bit range).
+ *
+ * @param $byte_count
+ * The number of random bytes to fetch and base64 encode.
+ *
+ * @return string
+ * The base64 encoded result will have a length of up to 4 * $byte_count.
+ */
+function drupal_random_key($byte_count = 32) {
+ return drupal_base64_encode(drupal_random_bytes($byte_count));
+}
+
+/**
+ * Returns a URL-safe, base64 encoded version of the supplied string.
+ *
+ * @param $string
+ * The string to convert to base64.
+ *
+ * @return string
+ */
+function drupal_base64_encode($string) {
+ $data = base64_encode($string);
+ // Modify the output so it's safe to use in URLs.
+ return strtr($data, array('+' => '-', '/' => '_', '=' => ''));
+}
+
/**
* Returns a string of highly randomized bytes (over the full 8-bit range).
*
@@ -1925,38 +1974,34 @@ function drupal_block_denied($ip) {
*/
function drupal_random_bytes($count) {
// $random_state does not use drupal_static as it stores random bytes.
- static $random_state, $bytes, $php_compatible;
- // Initialize on the first call. The contents of $_SERVER includes a mix of
- // user-specific and system information that varies a little with each page.
- if (!isset($random_state)) {
- $random_state = print_r($_SERVER, TRUE);
- if (function_exists('getmypid')) {
- // Further initialize with the somewhat random PHP process ID.
- $random_state .= getmypid();
- }
- $bytes = '';
- }
- if (strlen($bytes) < $count) {
+ static $random_state, $bytes, $has_openssl;
+
+ $missing_bytes = $count - strlen($bytes);
+
+ if ($missing_bytes > 0) {
// PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
// locking on Windows and rendered it unusable.
- if (!isset($php_compatible)) {
- $php_compatible = version_compare(PHP_VERSION, '5.3.4', '>=');
+ if (!isset($has_openssl)) {
+ $has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes');
}
- // /dev/urandom is available on many *nix systems and is considered the
- // best commonly available pseudo-random source.
- if ($fh = @fopen('/dev/urandom', 'rb')) {
+
+ // openssl_random_pseudo_bytes() will find entropy in a system-dependent
+ // way.
+ if ($has_openssl) {
+ $bytes .= openssl_random_pseudo_bytes($missing_bytes);
+ }
+
+ // Else, read directly from /dev/urandom, which is available on many *nix
+ // systems and is considered cryptographically secure.
+ elseif ($fh = @fopen('/dev/urandom', 'rb')) {
// PHP only performs buffered reads, so in reality it will always read
// at least 4096 bytes. Thus, it costs nothing extra to read and store
// that much so as to speed any additional invocations.
- $bytes .= fread($fh, max(4096, $count));
+ $bytes .= fread($fh, max(4096, $missing_bytes));
fclose($fh);
}
- // openssl_random_pseudo_bytes() will find entropy in a system-dependent
- // way.
- elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) {
- $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes));
- }
- // If /dev/urandom is not available or returns no bytes, this loop will
+
+ // If we couldn't get enough entropy, this simple hash-based PRNG will
// generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed
// through hash() prior to being rolled into $output, that the two hash()
@@ -1964,9 +2009,23 @@ function drupal_random_bytes($count) {
// the microtime() - is prepended rather than appended. This is to avoid
// directly leaking $random_state via the $output stream, which could
// allow for trivial prediction of further "random" numbers.
- while (strlen($bytes) < $count) {
- $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
- $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+ if (strlen($bytes) < $count) {
+ // Initialize on the first call. The contents of $_SERVER includes a mix of
+ // user-specific and system information that varies a little with each page.
+ if (!isset($random_state)) {
+ $random_state = print_r($_SERVER, TRUE);
+ if (function_exists('getmypid')) {
+ // Further initialize with the somewhat random PHP process ID.
+ $random_state .= getmypid();
+ }
+ $bytes = '';
+ }
+
+ do {
+ $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
+ $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+ }
+ while (strlen($bytes) < $count);
}
}
$output = substr($bytes, 0, $count);
@@ -1977,17 +2036,21 @@ function drupal_random_bytes($count) {
/**
* Calculates a base-64 encoded, URL-safe sha-256 hmac.
*
- * @param $data
+ * @param string $data
* String to be validated with the hmac.
- * @param $key
+ * @param string $key
* A secret string key.
*
- * @return
+ * @return string
* A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
* any = padding characters removed.
*/
function drupal_hmac_base64($data, $key) {
- $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
+ // Casting $data and $key to strings here is necessary to avoid empty string
+ // results of the hash function if they are not scalar values. As this
+ // function is used in security-critical contexts like token validation it is
+ // important that it never returns an empty string.
+ $hmac = base64_encode(hash_hmac('sha256', (string) $data, (string) $key, TRUE));
// Modify the hmac so it's safe to use in URLs.
return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
}
@@ -2088,7 +2151,7 @@ function drupal_array_merge_deep_array($arrays) {
* @return Object - the user object.
*/
function drupal_anonymous_user() {
- $user = new stdClass();
+ $user = variable_get('drupal_anonymous_user_object', new stdClass);
$user->uid = 0;
$user->hostname = ip_address();
$user->roles = array();
@@ -2107,7 +2170,7 @@ function drupal_anonymous_user() {
* drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
* @endcode
*
- * @param $phase
+ * @param int $phase
* A constant telling which phase to bootstrap to. When you bootstrap to a
* particular phase, all earlier phases are run automatically. Possible
* values:
@@ -2120,11 +2183,11 @@ function drupal_anonymous_user() {
* - DRUPAL_BOOTSTRAP_LANGUAGE: Finds out the language of the page.
* - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input
* data.
- * @param $new_phase
+ * @param boolean $new_phase
* A boolean, set to FALSE if calling drupal_bootstrap from inside a
* function called from drupal_bootstrap (recursion).
*
- * @return
+ * @return int
* The most recently completed phase.
*/
function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
@@ -2146,12 +2209,13 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
// bootstrap state.
static $stored_phase = -1;
- // When not recursing, store the phase name so it's not forgotten while
- // recursing.
- if ($new_phase) {
- $final_phase = $phase;
- }
if (isset($phase)) {
+ // When not recursing, store the phase name so it's not forgotten while
+ // recursing but take care of not going backwards.
+ if ($new_phase && $phase >= $stored_phase) {
+ $final_phase = $phase;
+ }
+
// Call a phase if it has not been called before and is below the requested
// phase.
while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) {
@@ -2218,6 +2282,19 @@ function drupal_get_user_timezone() {
}
}
+/**
+ * Gets a salt useful for hardening against SQL injection.
+ *
+ * @return
+ * A salt based on information in settings.php, not in the database.
+ */
+function drupal_get_hash_salt() {
+ global $drupal_hash_salt, $databases;
+ // If the $drupal_hash_salt variable is empty, a hash of the serialized
+ // database credentials is used as a fallback salt.
+ return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
+}
+
/**
* Provides custom PHP error handling.
*
@@ -2404,6 +2481,26 @@ function _drupal_bootstrap_variables() {
// Load bootstrap modules.
require_once DRUPAL_ROOT . '/includes/module.inc';
module_load_all(TRUE);
+
+ // Sanitize the destination parameter (which is often used for redirects) to
+ // prevent open redirect attacks leading to other domains. Sanitize both
+ // $_GET['destination'] and $_REQUEST['destination'] to protect code that
+ // relies on either, but do not sanitize $_POST to avoid interfering with
+ // unrelated form submissions. The sanitization happens here because
+ // url_is_external() requires the variable system to be available.
+ if (isset($_GET['destination']) || isset($_REQUEST['destination'])) {
+ require_once DRUPAL_ROOT . '/includes/common.inc';
+ // If the destination is an external URL, remove it.
+ if (isset($_GET['destination']) && url_is_external($_GET['destination'])) {
+ unset($_GET['destination']);
+ unset($_REQUEST['destination']);
+ }
+ // If there's still something in $_REQUEST['destination'] that didn't come
+ // from $_GET, check it too.
+ if (isset($_REQUEST['destination']) && (!isset($_GET['destination']) || $_REQUEST['destination'] != $_GET['destination']) && url_is_external($_REQUEST['destination'])) {
+ unset($_REQUEST['destination']);
+ }
+ }
}
/**
@@ -2426,7 +2523,7 @@ function _drupal_bootstrap_page_header() {
* @see drupal_bootstrap()
*/
function drupal_get_bootstrap_phase() {
- return drupal_bootstrap();
+ return drupal_bootstrap(NULL, FALSE);
}
/**
@@ -2438,7 +2535,6 @@ function drupal_get_bootstrap_phase() {
* HMAC and timestamp.
*/
function drupal_valid_test_ua() {
- global $drupal_hash_salt;
// No reason to reset this.
static $test_prefix;
@@ -2452,7 +2548,7 @@ function drupal_valid_test_ua() {
// We use the salt from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
- $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
$time_diff = REQUEST_TIME - $time;
// Since we are making a local request a 5 second time window is allowed,
// and the HMAC must match.
@@ -2470,14 +2566,13 @@ function drupal_valid_test_ua() {
* Generates a user agent string with a HMAC and timestamp for simpletest.
*/
function drupal_generate_test_ua($prefix) {
- global $drupal_hash_salt;
static $key;
if (!isset($key)) {
// We use the salt from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
- $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
}
// Generate a moderately secure HMAC based on the database credentials.
$salt = uniqid('', TRUE);
@@ -2542,7 +2637,7 @@ function drupal_installation_attempted() {
*
* This would include implementations of hook_install(), which could run
* during the Drupal installation phase, and might also be run during
- * non-installation time, such as while installing the module from the the
+ * non-installation time, such as while installing the module from the
* module administration page.
*
* Example usage:
@@ -3071,10 +3166,13 @@ function _registry_check_code($type, $name = NULL) {
// This function may get called when the default database is not active, but
// there is no reason we'd ever want to not use the default database for
// this query.
- $file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array(
- ':name' => $name,
- ':type' => $type,
- ))
+ $file = Database::getConnection('default', 'default')
+ ->select('registry', 'r', array('target' => 'default'))
+ ->fields('r', array('filename'))
+ // Use LIKE here to make the query case-insensitive.
+ ->condition('r.name', db_like($name), 'LIKE')
+ ->condition('r.type', $type)
+ ->execute()
->fetchField();
// Flag that we've run a lookup query and need to update the cache.
@@ -3222,8 +3320,8 @@ function registry_update() {
* However, the above line of code does not work, because PHP only allows static
* variables to be initializied by literal values, and does not allow static
* variables to be assigned to references.
- * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
- * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references
+ * - http://php.net/manual/language.variables.scope.php#language.variables.scope.static
+ * - http://php.net/manual/language.variables.scope.php#language.variables.scope.references
* The example below shows the syntax needed to work around both limitations.
* For benchmarks and more information, see http://drupal.org/node/619666.
*
@@ -3248,11 +3346,9 @@ function registry_update() {
* @param $default_value
* Optional default value.
* @param $reset
- * TRUE to reset a specific named variable, or all variables if $name is NULL.
- * Resetting every variable should only be used, for example, for running
- * unit tests with a clean environment. Should be used only though via
- * function drupal_static_reset() and the return value should not be used in
- * this case.
+ * TRUE to reset one or all variables(s). This parameter is only used
+ * internally and should not be passed in; use drupal_static_reset() instead.
+ * (This function's return value should not be used when TRUE is passed in.)
*
* @return
* Returns a variable by reference.
@@ -3297,6 +3393,8 @@ function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
*
* @param $name
* Name of the static variable to reset. Omit to reset all variables.
+ * Resetting all variables should only be used, for example, for running unit
+ * tests with a clean environment.
*/
function drupal_static_reset($name = NULL) {
drupal_static($name, NULL, TRUE);
@@ -3383,3 +3481,63 @@ function _drupal_shutdown_function() {
}
}
}
+
+/**
+ * Compares the memory required for an operation to the available memory.
+ *
+ * @param $required
+ * The memory required for the operation, expressed as a number of bytes with
+ * optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8bytes,
+ * 9mbytes).
+ * @param $memory_limit
+ * (optional) The memory limit for the operation, expressed as a number of
+ * bytes with optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G,
+ * 6GiB, 8bytes, 9mbytes). If no value is passed, the current PHP
+ * memory_limit will be used. Defaults to NULL.
+ *
+ * @return
+ * TRUE if there is sufficient memory to allow the operation, or FALSE
+ * otherwise.
+ */
+function drupal_check_memory_limit($required, $memory_limit = NULL) {
+ if (!isset($memory_limit)) {
+ $memory_limit = ini_get('memory_limit');
+ }
+
+ // There is sufficient memory if:
+ // - No memory limit is set.
+ // - The memory limit is set to unlimited (-1).
+ // - The memory limit is greater than the memory required for the operation.
+ return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
+}
+
+/**
+ * Invalidates a PHP file from any active opcode caches.
+ *
+ * If the opcode cache does not support the invalidation of individual files,
+ * the entire cache will be flushed.
+ *
+ * @param string $filepath
+ * The absolute path of the PHP file to invalidate.
+ */
+function drupal_clear_opcode_cache($filepath) {
+ if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
+ // Below PHP 5.3, clearstatcache does not accept any function parameters.
+ clearstatcache();
+ }
+ else {
+ clearstatcache(TRUE, $filepath);
+ }
+
+ // Zend OPcache.
+ if (function_exists('opcache_invalidate')) {
+ opcache_invalidate($filepath, TRUE);
+ }
+ // APC.
+ if (function_exists('apc_delete_file')) {
+ // apc_delete_file() throws a PHP warning in case the specified file was
+ // not compiled yet.
+ // @see http://php.net/apc-delete-file
+ @apc_delete_file($filepath);
+ }
+}
diff --git a/includes/cache.inc b/includes/cache.inc
index a19d3c3..207bf66 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -80,43 +80,15 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
* same name. Other implementations might want to store several bins in data
* structures that get flushed together. While it is not a problem for most
* cache bins if the entries in them are flushed before their expire time, some
- * might break functionality or are extremely expensive to recalculate. These
- * will be marked with a (*). The other bins expired automatically by core.
- * Contributed modules can add additional bins and get them expired
- * automatically by implementing hook_flush_caches().
- *
- * - cache: Generic cache storage bin (used for variables, theme registry,
- * locale date, list of simpletest tests etc).
- *
- * - cache_block: Stores the content of various blocks.
- *
- * - cache field: Stores the field data belonging to a given object.
- *
- * - cache_filter: Stores filtered pieces of content.
- *
- * - cache_form(*): Stores multistep forms. Flushing this bin means that some
- * forms displayed to users lose their state and the data already submitted
- * to them.
- *
- * - cache_menu: Stores the structure of visible navigation menus per page.
- *
- * - cache_page: Stores generated pages for anonymous users. It is flushed
- * very often, whenever a page changes, at least for every ode and comment
- * submission. This is the only bin affected by the page cache setting on
- * the administrator panel.
- *
- * - cache path: Stores the system paths that have an alias.
- *
- * - cache update(*): Stores available releases. The update server (for
- * example, drupal.org) needs to produce the relevant XML for every project
- * installed on the current site. As this is different for (almost) every
- * site, it's very expensive to recalculate for the update server.
+ * might break functionality or are extremely expensive to recalculate. The
+ * other bins are expired automatically by core. Contributed modules can add
+ * additional bins and get them expired automatically by implementing
+ * hook_flush_caches().
*
* The reasons for having several bins are as follows:
- *
- * - smaller bins mean smaller database tables and allow for faster selects and
- * inserts
- * - we try to put fast changing cache items and rather static ones into
+ * - Smaller bins mean smaller database tables and allow for faster selects and
+ * inserts.
+ * - We try to put fast changing cache items and rather static ones into
* different bins. The effect is that only the fast changing bins will need a
* lot of writes to disk. The more static bins will also be better cacheable
* with MySQL's query cache.
@@ -125,15 +97,31 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
- * serialized before insertion.
- * Strings will be stored as plain text and not serialized.
+ * serialized before insertion. Strings will be stored as plain text and are
+ * not serialized. Some storage engines only allow objects up to a maximum of
+ * 1MB in size to be stored by default. When caching large arrays or similar,
+ * take care to ensure $data does not exceed this size.
* @param $bin
- * The cache bin to store the data in. Valid core values are 'cache_block',
- * 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
- * 'cache_menu', 'cache_page', 'cache_update' or 'cache' for the default
- * cache.
+ * (optional) The cache bin to store the data in. Valid core values are:
+ * - cache: (default) Generic cache storage bin (used for theme registry,
+ * locale date, list of simpletest tests, etc.).
+ * - cache_block: Stores the content of various blocks.
+ * - cache_bootstrap: Stores the class registry, the system list of modules,
+ * the list of which modules implement which hooks, and the Drupal variable
+ * list.
+ * - cache_field: Stores the field data belonging to a given object.
+ * - cache_filter: Stores filtered pieces of content.
+ * - cache_form: Stores multistep forms. Flushing this bin means that some
+ * forms displayed to users lose their state and the data already submitted
+ * to them. This bin should not be flushed before its expired time.
+ * - cache_menu: Stores the structure of visible navigation menus per page.
+ * - cache_page: Stores generated pages for anonymous users. It is flushed
+ * very often, whenever a page changes, at least for every node and comment
+ * submission. This is the only bin affected by the page cache setting on
+ * the administrator panel.
+ * - cache_path: Stores the system paths that have an alias.
* @param $expire
- * One of the following values:
+ * (optional) One of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
* explicitly told to using cache_clear_all() with a cache ID.
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
@@ -141,6 +129,7 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
* - A Unix timestamp: Indicates that the item should be kept at least until
* the given time, after which it behaves like CACHE_TEMPORARY.
*
+ * @see _update_cache_set()
* @see cache_get()
*/
function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
@@ -150,18 +139,20 @@ function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
/**
* Expires data from the cache.
*
- * If called without arguments, expirable entries will be cleared from the
- * cache_page and cache_block bins.
+ * If called with the arguments $cid and $bin set to NULL or omitted, then
+ * expirable entries will be cleared from the cache_page and cache_block bins,
+ * and the $wildcard argument is ignored.
*
* @param $cid
- * If set, the cache ID to delete. Otherwise, all cache entries that can
- * expire are deleted.
+ * If set, the cache ID or an array of cache IDs. Otherwise, all cache entries
+ * that can expire are deleted. The $wildcard argument will be ignored if set
+ * to NULL.
* @param $bin
* If set, the cache bin to delete from. Mandatory argument if $cid is set.
* @param $wildcard
- * If TRUE, cache IDs starting with $cid are deleted in addition to the
- * exact cache ID specified by $cid. If $wildcard is TRUE and $cid is '*',
- * the entire cache bin is emptied.
+ * If TRUE, the $cid argument must contain a string value and cache IDs
+ * starting with $cid are deleted in addition to the exact cache ID specified
+ * by $cid. If $wildcard is TRUE and $cid is '*', the entire cache is emptied.
*/
function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
if (!isset($cid) && !isset($bin)) {
@@ -230,13 +221,6 @@ function cache_is_empty($bin) {
* @see DrupalDatabaseCache
*/
interface DrupalCacheInterface {
- /**
- * Constructs a new cache interface.
- *
- * @param $bin
- * The cache bin for which the object is created.
- */
- function __construct($bin);
/**
* Returns data from the persistent cache.
@@ -272,10 +256,12 @@ interface DrupalCacheInterface {
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
- * serialized before insertion.
- * Strings will be stored as plain text and not serialized.
+ * serialized before insertion. Strings will be stored as plain text and not
+ * serialized. Some storage engines only allow objects up to a maximum of
+ * 1MB in size to be stored by default. When caching large arrays or
+ * similar, take care to ensure $data does not exceed this size.
* @param $expire
- * One of the following values:
+ * (optional) One of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
* explicitly told to using cache_clear_all() with a cache ID.
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
@@ -293,12 +279,14 @@ interface DrupalCacheInterface {
* cache_page and cache_block bins.
*
* @param $cid
- * If set, the cache ID to delete. Otherwise, all cache entries that can
- * expire are deleted.
+ * If set, the cache ID or an array of cache IDs. Otherwise, all cache
+ * entries that can expire are deleted. The $wildcard argument will be
+ * ignored if set to NULL.
* @param $wildcard
- * If set to TRUE, the $cid is treated as a substring
- * to match rather than a complete ID. The match is a right hand
- * match. If '*' is given as $cid, the bin $bin will be emptied.
+ * If TRUE, the $cid argument must contain a string value and cache IDs
+ * starting with $cid are deleted in addition to the exact cache ID
+ * specified by $cid. If $wildcard is TRUE and $cid is '*', the entire
+ * cache is emptied.
*/
function clear($cid = NULL, $wildcard = FALSE);
@@ -324,7 +312,10 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
protected $bin;
/**
- * Constructs a new DrupalDatabaseCache object.
+ * Constructs a DrupalDatabaseCache object.
+ *
+ * @param $bin
+ * The cache bin for which the object is created.
*/
function __construct($bin) {
$this->bin = $bin;
@@ -518,7 +509,16 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
else {
if ($wildcard) {
if ($cid == '*') {
- db_truncate($this->bin)->execute();
+ // Check if $this->bin is a cache table before truncating. Other
+ // cache_clear_all() operations throw a PDO error in this situation,
+ // so we don't need to verify them first. This ensures that non-cache
+ // tables cannot be truncated accidentally.
+ if ($this->isValidBin()) {
+ db_truncate($this->bin)->execute();
+ }
+ else {
+ throw new Exception(t('Invalid or missing cache bin specified: %bin', array('%bin' => $this->bin)));
+ }
}
else {
db_delete($this->bin)
@@ -555,4 +555,25 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
->fetchField();
return empty($result);
}
+
+ /**
+ * Checks if $this->bin represents a valid cache table.
+ *
+ * This check is required to ensure that non-cache tables are not truncated
+ * accidentally when calling cache_clear_all().
+ *
+ * @return boolean
+ */
+ function isValidBin() {
+ if ($this->bin == 'cache' || substr($this->bin, 0, 6) == 'cache_') {
+ // Skip schema check for bins with standard table names.
+ return TRUE;
+ }
+ // These fields are required for any cache table.
+ $fields = array('cid', 'data', 'expire', 'created', 'serialized');
+ // Load the table schema.
+ $schema = drupal_get_schema($this->bin);
+ // Confirm that all fields are present.
+ return isset($schema['fields']) && !array_diff($fields, array_keys($schema['fields']));
+ }
}
diff --git a/includes/common.inc b/includes/common.inc
index 8276576..0437ec1 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -281,7 +281,7 @@ function drupal_get_rdf_namespaces() {
/**
* Adds output to the HEAD tag of the HTML page.
*
- * This function can be called as long the headers aren't sent. Pass no
+ * This function can be called as long as the headers aren't sent. Pass no
* arguments (or NULL for both) to retrieve the currently stored elements.
*
* @param $data
@@ -458,7 +458,7 @@ function drupal_get_query_array($query) {
$result = array();
if (!empty($query)) {
foreach (explode('&', $query) as $param) {
- $param = explode('=', $param);
+ $param = explode('=', $param, 2);
$result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
}
}
@@ -544,37 +544,32 @@ function drupal_get_destination() {
}
/**
- * Parses a system URL string into an associative array suitable for url().
+ * Parses a URL string into its path, query, and fragment components.
*
- * This function should only be used for URLs that have been generated by the
- * system, such as via url(). It should not be used for URLs that come from
- * external sources, or URLs that link to external resources.
+ * This function splits both internal paths like @code node?b=c#d @endcode and
+ * external URLs like @code https://example.com/a?b=c#d @endcode into their
+ * component parts. See
+ * @link http://tools.ietf.org/html/rfc3986#section-3 RFC 3986 @endlink for an
+ * explanation of what the component parts are.
*
- * The returned array contains a 'path' that may be passed separately to url().
- * For example:
- * @code
- * $options = drupal_parse_url($_GET['destination']);
- * $my_url = url($options['path'], $options);
- * $my_link = l('Example link', $options['path'], $options);
- * @endcode
+ * Note that, unlike the RFC, when passed an external URL, this function
+ * groups the scheme, authority, and path together into the path component.
*
- * This is required, because url() does not support relative URLs containing a
- * query string or fragment in its $path argument. Instead, any query string
- * needs to be parsed into an associative query parameter array in
- * $options['query'] and the fragment into $options['fragment'].
+ * @param string $url
+ * The internal path or external URL string to parse.
*
- * @param $url
- * The URL string to parse, f.e. $_GET['destination'].
+ * @return array
+ * An associative array containing:
+ * - path: The path component of $url. If $url is an external URL, this
+ * includes the scheme, authority, and path.
+ * - query: An array of query parameters from $url, if they exist.
+ * - fragment: The fragment component from $url, if it exists.
*
- * @return
- * An associative array containing the keys:
- * - 'path': The path of the URL. If the given $url is external, this includes
- * the scheme and host.
- * - 'query': An array of query parameters of $url, if existent.
- * - 'fragment': The fragment of $url, if existent.
- *
- * @see url()
* @see drupal_goto()
+ * @see l()
+ * @see url()
+ * @see http://tools.ietf.org/html/rfc3986
+ *
* @ingroup php_wrappers
*/
function drupal_parse_url($url) {
@@ -641,7 +636,7 @@ function drupal_encode_path($path) {
}
/**
- * Sends the user to a different Drupal page.
+ * Sends the user to a different page.
*
* This issues an on-site HTTP redirect. The function makes sure the redirected
* URL is formatted correctly.
@@ -785,6 +780,13 @@ function drupal_access_denied() {
* - data: A string containing the response body that was received.
*/
function drupal_http_request($url, array $options = array()) {
+ // Allow an alternate HTTP client library to replace Drupal's default
+ // implementation.
+ $override_function = variable_get('drupal_http_request_function', FALSE);
+ if (!empty($override_function) && function_exists($override_function)) {
+ return $override_function($url, $options);
+ }
+
$result = new stdClass();
// Parse the URL and make sure we can handle the schema.
@@ -922,7 +924,7 @@ function drupal_http_request($url, array $options = array()) {
// If the server URL has a user then attempt to use basic authentication.
if (isset($uri['user'])) {
- $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
+ $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ':'));
}
// If the database prefix is being used by SimpleTest to run the tests in a copied
@@ -983,9 +985,10 @@ function drupal_http_request($url, array $options = array()) {
$response = preg_split("/\r\n|\n|\r/", $response);
// Parse the response status line.
- list($protocol, $code, $status_message) = explode(' ', trim(array_shift($response)), 3);
- $result->protocol = $protocol;
- $result->status_message = $status_message;
+ $response_status_array = _drupal_parse_response_status(trim(array_shift($response)));
+ $result->protocol = $response_status_array['http_version'];
+ $result->status_message = $response_status_array['reason_phrase'];
+ $code = $response_status_array['response_code'];
$result->headers = array();
@@ -1076,12 +1079,43 @@ function drupal_http_request($url, array $options = array()) {
}
break;
default:
- $result->error = $status_message;
+ $result->error = $result->status_message;
}
return $result;
}
+/**
+ * Splits an HTTP response status line into components.
+ *
+ * See the @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html status line definition @endlink
+ * in RFC 2616.
+ *
+ * @param string $respone
+ * The response status line, for example 'HTTP/1.1 500 Internal Server Error'.
+ *
+ * @return array
+ * Keyed array containing the component parts. If the response is malformed,
+ * all possible parts will be extracted. 'reason_phrase' could be empty.
+ * Possible keys:
+ * - 'http_version'
+ * - 'response_code'
+ * - 'reason_phrase'
+ */
+function _drupal_parse_response_status($response) {
+ $response_array = explode(' ', trim($response), 3);
+ // Set up empty values.
+ $result = array(
+ 'reason_phrase' => '',
+ );
+ $result['http_version'] = $response_array[0];
+ $result['response_code'] = $response_array[1];
+ if (isset($response_array[2])) {
+ $result['reason_phrase'] = $response_array[2];
+ }
+ return $result;
+}
+
/**
* Helper function for determining hosts excluded from needing a proxy.
*
@@ -1127,7 +1161,7 @@ function _fix_gpc_magic(&$item) {
* @param $key
* The key for the item within $_FILES.
*
- * @see http://php.net/manual/en/features.file-upload.php#42280
+ * @see http://php.net/manual/features.file-upload.php#42280
*/
function _fix_gpc_magic_files(&$item, $key) {
if ($key != 'tmp_name') {
@@ -1167,7 +1201,8 @@ function fix_gpc_magic() {
/**
* Verifies the syntax of the given e-mail address.
*
- * See @link http://tools.ietf.org/html/rfc5321 RFC 5321 @endlink for details.
+ * This uses the
+ * @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
*
* @param $mail
* A string containing an e-mail address.
@@ -1418,7 +1453,6 @@ function filter_xss_admin($string) {
* valid UTF-8.
*
* @see drupal_validate_utf8()
- * @ingroup sanitization
*/
function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
@@ -1942,7 +1976,7 @@ function format_interval($interval, $granularity = 2, $langcode = NULL) {
* get interpreted as date format characters.
* @param $timezone
* (optional) Time zone identifier, as described at
- * http://php.net/manual/en/timezones.php Defaults to the time zone used to
+ * http://php.net/manual/timezones.php Defaults to the time zone used to
* display the page.
* @param $langcode
* (optional) Language code to translate to. Defaults to the language used to
@@ -2078,6 +2112,9 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
/**
* Format a username.
*
+ * This is also the label callback implementation of
+ * callback_entity_info_label() for user_entity_info().
+ *
* By default, the passed-in object's 'name' property is used if it exists, or
* else, the site-defined value for the 'anonymous' variable. However, a module
* may override this by implementing hook_username_alter(&$name, $account).
@@ -2177,14 +2214,20 @@ function url($path = NULL, array $options = array()) {
'prefix' => ''
);
+ // A duplicate of the code from url_is_external() to avoid needing another
+ // function call, since performance inside url() is critical.
if (!isset($options['external'])) {
- // Return an external link if $path contains an allowed absolute URL. Only
- // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
- // before any / ? or #. Note: we could use url_is_external($path) here, but
- // that would require another function call, and performance inside url() is
- // critical.
+ // Return an external link if $path contains an allowed absolute URL. Avoid
+ // calling drupal_strip_dangerous_protocols() if there is any slash (/),
+ // hash (#) or question_mark (?) before the colon (:) occurrence - if any -
+ // as this would clearly mean it is not a URL. If the path starts with 2
+ // slashes then it is always considered an external URL without an explicit
+ // protocol part.
$colonpos = strpos($path, ':');
- $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
+ $options['external'] = (strpos($path, '//') === 0)
+ || ($colonpos !== FALSE
+ && !preg_match('![/?#]!', substr($path, 0, $colonpos))
+ && drupal_strip_dangerous_protocols($path) == $path);
}
// Preserve the original path before altering or aliasing.
@@ -2222,6 +2265,11 @@ function url($path = NULL, array $options = array()) {
return $path . $options['fragment'];
}
+ // Strip leading slashes from internal paths to prevent them becoming external
+ // URLs without protocol. /example.com should not be turned into
+ // //example.com.
+ $path = ltrim($path, '/');
+
global $base_url, $base_secure_url, $base_insecure_url;
// The base_url might be rewritten from the language rewrite in domain mode.
@@ -2299,10 +2347,15 @@ function url($path = NULL, array $options = array()) {
*/
function url_is_external($path) {
$colonpos = strpos($path, ':');
- // Avoid calling drupal_strip_dangerous_protocols() if there is any
- // slash (/), hash (#) or question_mark (?) before the colon (:)
- // occurrence - if any - as this would clearly mean it is not a URL.
- return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path;
+ // Avoid calling drupal_strip_dangerous_protocols() if there is any slash (/),
+ // hash (#) or question_mark (?) before the colon (:) occurrence - if any - as
+ // this would clearly mean it is not a URL. If the path starts with 2 slashes
+ // then it is always considered an external URL without an explicit protocol
+ // part.
+ return (strpos($path, '//') === 0)
+ || ($colonpos !== FALSE
+ && !preg_match('![/?#]!', substr($path, 0, $colonpos))
+ && drupal_strip_dangerous_protocols($path) == $path);
}
/**
@@ -2379,6 +2432,14 @@ function drupal_attributes(array $attributes = array()) {
* internal links output by modules should be generated by this function if
* possible.
*
+ * However, for links enclosed in translatable text you should use t() and
+ * embed the HTML anchor tag directly in the translated string. For example:
+ * @code
+ * t('Visit the settings page', array('@url' => url('admin')));
+ * @endcode
+ * This keeps the context of the link title ('settings' in the example) for
+ * translators.
+ *
* @param string $text
* The translated link text for the anchor tag.
* @param string $path
@@ -2591,7 +2652,10 @@ function drupal_deliver_html_page($page_callback_result) {
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
- $_GET['destination'] = $_GET['q'];
+ // Make sure that the current path is not interpreted as external URL.
+ if (!url_is_external($_GET['q'])) {
+ $_GET['destination'] = $_GET['q'];
+ }
}
$path = drupal_get_normal_path(variable_get('site_404', ''));
@@ -2620,7 +2684,10 @@ function drupal_deliver_html_page($page_callback_result) {
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
- $_GET['destination'] = $_GET['q'];
+ // Make sure that the current path is not interpreted as external URL.
+ if (!url_is_external($_GET['q'])) {
+ $_GET['destination'] = $_GET['q'];
+ }
}
$path = drupal_get_normal_path(variable_get('site_403', ''));
@@ -2779,7 +2846,7 @@ function drupal_set_time_limit($time_limit) {
* The name of the item for which the path is requested.
*
* @return
- * The path to the requested item.
+ * The path to the requested item or an empty string if the item is not found.
*/
function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
@@ -3429,7 +3496,11 @@ function drupal_pre_render_styles($elements) {
$import_batch = array_slice($import, 0, 31);
$import = array_slice($import, 31);
$element = $style_element_defaults;
- $element['#value'] = implode("\n", $import_batch);
+ // This simplifies the JavaScript regex, allowing each line
+ // (separated by \n) to be treated as a completely different string.
+ // This means that we can use ^ and $ on one line at a time, and not
+ // worry about style tags since they'll never match the regex.
+ $element['#value'] = "\n" . implode("\n", $import_batch) . "\n";
$element['#attributes']['media'] = $group['media'];
$element['#browsers'] = $group['browsers'];
$elements[] = $element;
@@ -3654,17 +3725,23 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
if ($basepath && !file_uri_scheme($file)) {
$file = $basepath . '/' . $file;
}
+ // Store the parent base path to restore it later.
+ $parent_base_path = $basepath;
+ // Set the current base path to process possible child imports.
$basepath = dirname($file);
// Load the CSS stylesheet. We suppress errors because themes may specify
// stylesheets in their .info file that don't exist in the theme's path,
// but are merely there to disable certain module CSS files.
+ $content = '';
if ($contents = @file_get_contents($file)) {
// Return the processed stylesheet.
- return drupal_load_stylesheet_content($contents, $_optimize);
+ $content = drupal_load_stylesheet_content($contents, $_optimize);
}
- return '';
+ // Restore the parent base path as the file and its childen are processed.
+ $basepath = $parent_base_path;
+ return $content;
}
/**
@@ -3681,7 +3758,7 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
*/
function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
// Remove multiple charset declarations for standards compliance (and fixing Safari problems).
- $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
+ $contents = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $contents);
if ($optimize) {
// Perform some safe CSS optimizations.
@@ -3700,7 +3777,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
// Remove certain whitespace.
// There are different conditions for removing leading and trailing
// whitespace.
- // @see http://php.net/manual/en/regexp.reference.subpatterns.php
+ // @see http://php.net/manual/regexp.reference.subpatterns.php
$contents = preg_replace('<
# Strip leading and trailing whitespace.
\s*([@{};,])\s*
@@ -3749,7 +3826,7 @@ function _drupal_load_stylesheet($matches) {
// Alter all internal url() paths. Leave external paths alone. We don't need
// to normalize absolute paths here (i.e. remove folder/... segments) because
// that will be done later.
- return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file);
+ return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)([^\'")]+)([\'"]?)\s*\)/i', 'url(\1' . $directory . '\2\3)', $file);
}
/**
@@ -3814,7 +3891,14 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
* The cleaned class name.
*/
function drupal_html_class($class) {
- return drupal_clean_css_identifier(drupal_strtolower($class));
+ // The output of this function will never change, so this uses a normal
+ // static instead of drupal_static().
+ static $classes = array();
+
+ if (!isset($classes[$class])) {
+ $classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
+ }
+ return $classes[$class];
}
/**
@@ -3869,7 +3953,16 @@ function drupal_html_id($id) {
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
// returned by this function, potentially with the appended counter, so
// we parse that to reconstruct the $seen_ids array.
- foreach ($_POST['ajax_html_ids'] as $seen_id) {
+ if (isset($_POST['ajax_html_ids'][0]) && strpos($_POST['ajax_html_ids'][0], ',') === FALSE) {
+ $ajax_html_ids = $_POST['ajax_html_ids'];
+ }
+ else {
+ // jquery.form.js may send the server a comma-separated string as the
+ // first element of an array (see http://drupal.org/node/1575060), so
+ // we need to convert it to an array in that case.
+ $ajax_html_ids = explode(',', $_POST['ajax_html_ids'][0]);
+ }
+ foreach ($ajax_html_ids as $seen_id) {
// We rely on '--' being used solely for separating a base id from the
// counter, which this function ensures when returning an id.
$parts = explode('--', $seen_id, 2);
@@ -4069,7 +4162,14 @@ function drupal_region_class($region) {
* else being the same, JavaScript added by a call to drupal_add_js() that
* happened later in the page request gets added to the page after one for
* which drupal_add_js() happened earlier in the page request.
- * - defer: If set to TRUE, the defer attribute is set on the <script>
+ * - requires_jquery: Set this to FALSE if the JavaScript you are adding does
+ * not have a dependency on jQuery. Defaults to TRUE, except for JavaScript
+ * settings where it defaults to FALSE. This is used on sites that have the
+ * 'javascript_always_use_jquery' variable set to FALSE; on those sites, if
+ * all the JavaScript added to the page by drupal_add_js() does not have a
+ * dependency on jQuery, then for improved front-end performance Drupal
+ * will not add jQuery and related libraries and settings to the page.
+ * - defer: If set to TRUE, the defer attribute is set on the ';
+ filter_format_save($format);
+ user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(filter_permission_name($format) => 1));
+ $this->drupalGet('filter/tips');
+ $this->assertNoRaw($format->name, 'Text format name contains no xss.');
}
/**
- * Verify that a text format is properly stored.
+ * Verifies that a text format is properly stored.
*/
function verifyTextFormat($format) {
$t_args = array('%format' => $format->name);
@@ -83,17 +92,17 @@ class FilterCRUDTestCase extends DrupalWebTestCase {
->condition('format', $format->format)
->execute()
->fetchObject();
- $this->assertEqual($db_format->format, $format->format, t('Database: Proper format id for text format %format.', $t_args));
- $this->assertEqual($db_format->name, $format->name, t('Database: Proper title for text format %format.', $t_args));
- $this->assertEqual($db_format->cache, $format->cache, t('Database: Proper cache indicator for text format %format.', $t_args));
- $this->assertEqual($db_format->weight, $format->weight, t('Database: Proper weight for text format %format.', $t_args));
+ $this->assertEqual($db_format->format, $format->format, format_string('Database: Proper format id for text format %format.', $t_args));
+ $this->assertEqual($db_format->name, $format->name, format_string('Database: Proper title for text format %format.', $t_args));
+ $this->assertEqual($db_format->cache, $format->cache, format_string('Database: Proper cache indicator for text format %format.', $t_args));
+ $this->assertEqual($db_format->weight, $format->weight, format_string('Database: Proper weight for text format %format.', $t_args));
// Verify filter_format_load().
$filter_format = filter_format_load($format->format);
- $this->assertEqual($filter_format->format, $format->format, t('filter_format_load: Proper format id for text format %format.', $t_args));
- $this->assertEqual($filter_format->name, $format->name, t('filter_format_load: Proper title for text format %format.', $t_args));
- $this->assertEqual($filter_format->cache, $format->cache, t('filter_format_load: Proper cache indicator for text format %format.', $t_args));
- $this->assertEqual($filter_format->weight, $format->weight, t('filter_format_load: Proper weight for text format %format.', $t_args));
+ $this->assertEqual($filter_format->format, $format->format, format_string('filter_format_load: Proper format id for text format %format.', $t_args));
+ $this->assertEqual($filter_format->name, $format->name, format_string('filter_format_load: Proper title for text format %format.', $t_args));
+ $this->assertEqual($filter_format->cache, $format->cache, format_string('filter_format_load: Proper cache indicator for text format %format.', $t_args));
+ $this->assertEqual($filter_format->weight, $format->weight, format_string('filter_format_load: Proper weight for text format %format.', $t_args));
// Verify the 'cache' text format property according to enabled filters.
$filter_info = filter_get_filters();
@@ -107,11 +116,11 @@ class FilterCRUDTestCase extends DrupalWebTestCase {
break;
}
}
- $this->assertEqual($filter_format->cache, $cacheable, t('Text format contains proper cache property.'));
+ $this->assertEqual($filter_format->cache, $cacheable, 'Text format contains proper cache property.');
}
/**
- * Verify that filters are properly stored for a text format.
+ * Verifies that filters are properly stored for a text format.
*/
function verifyFilters($format) {
// Verify filter database records.
@@ -121,20 +130,20 @@ class FilterCRUDTestCase extends DrupalWebTestCase {
$t_args = array('%format' => $format->name, '%filter' => $name);
// Verify that filter status is properly stored.
- $this->assertEqual($filter->status, $format_filters[$name]['status'], t('Database: Proper status for %filter in text format %format.', $t_args));
+ $this->assertEqual($filter->status, $format_filters[$name]['status'], format_string('Database: Proper status for %filter in text format %format.', $t_args));
// Verify that filter settings were properly stored.
- $this->assertEqual(unserialize($filter->settings), isset($format_filters[$name]['settings']) ? $format_filters[$name]['settings'] : array(), t('Database: Proper filter settings for %filter in text format %format.', $t_args));
+ $this->assertEqual(unserialize($filter->settings), isset($format_filters[$name]['settings']) ? $format_filters[$name]['settings'] : array(), format_string('Database: Proper filter settings for %filter in text format %format.', $t_args));
// Verify that each filter has a module name assigned.
- $this->assertTrue(!empty($filter->module), t('Database: Proper module name for %filter in text format %format.', $t_args));
+ $this->assertTrue(!empty($filter->module), format_string('Database: Proper module name for %filter in text format %format.', $t_args));
// Remove the filter from the copy of saved $format to check whether all
// filters have been processed later.
unset($format_filters[$name]);
}
// Verify that all filters have been processed.
- $this->assertTrue(empty($format_filters), t('Database contains values for all filters in the saved format.'));
+ $this->assertTrue(empty($format_filters), 'Database contains values for all filters in the saved format.');
// Verify filter_list_format().
$filters = filter_list_format($format->format);
@@ -143,23 +152,26 @@ class FilterCRUDTestCase extends DrupalWebTestCase {
$t_args = array('%format' => $format->name, '%filter' => $name);
// Verify that filter status is properly stored.
- $this->assertEqual($filter->status, $format_filters[$name]['status'], t('filter_list_format: Proper status for %filter in text format %format.', $t_args));
+ $this->assertEqual($filter->status, $format_filters[$name]['status'], format_string('filter_list_format: Proper status for %filter in text format %format.', $t_args));
// Verify that filter settings were properly stored.
- $this->assertEqual($filter->settings, isset($format_filters[$name]['settings']) ? $format_filters[$name]['settings'] : array(), t('filter_list_format: Proper filter settings for %filter in text format %format.', $t_args));
+ $this->assertEqual($filter->settings, isset($format_filters[$name]['settings']) ? $format_filters[$name]['settings'] : array(), format_string('filter_list_format: Proper filter settings for %filter in text format %format.', $t_args));
// Verify that each filter has a module name assigned.
- $this->assertTrue(!empty($filter->module), t('filter_list_format: Proper module name for %filter in text format %format.', $t_args));
+ $this->assertTrue(!empty($filter->module), format_string('filter_list_format: Proper module name for %filter in text format %format.', $t_args));
// Remove the filter from the copy of saved $format to check whether all
// filters have been processed later.
unset($format_filters[$name]);
}
// Verify that all filters have been processed.
- $this->assertTrue(empty($format_filters), t('filter_list_format: Loaded filters contain values for all filters in the saved format.'));
+ $this->assertTrue(empty($format_filters), 'filter_list_format: Loaded filters contain values for all filters in the saved format.');
}
}
+/**
+ * Tests the administrative functionality of the Filter module.
+ */
class FilterAdminTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -185,6 +197,9 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$this->drupalLogin($this->admin_user);
}
+ /**
+ * Tests the format administration functionality.
+ */
function testFormatAdmin() {
// Add text format.
$this->drupalGet('admin/config/content/formats');
@@ -199,14 +214,14 @@ class FilterAdminTestCase extends DrupalWebTestCase {
// Verify default weight of the text format.
$this->drupalGet('admin/config/content/formats');
- $this->assertFieldByName("formats[$format_id][weight]", 0, t('Text format weight was saved.'));
+ $this->assertFieldByName("formats[$format_id][weight]", 0, 'Text format weight was saved.');
// Change the weight of the text format.
$edit = array(
"formats[$format_id][weight]" => 5,
);
$this->drupalPost('admin/config/content/formats', $edit, t('Save changes'));
- $this->assertFieldByName("formats[$format_id][weight]", 5, t('Text format weight was saved.'));
+ $this->assertFieldByName("formats[$format_id][weight]", 5, 'Text format weight was saved.');
// Edit text format.
$this->drupalGet('admin/config/content/formats');
@@ -216,7 +231,7 @@ class FilterAdminTestCase extends DrupalWebTestCase {
// Verify that the custom weight of the text format has been retained.
$this->drupalGet('admin/config/content/formats');
- $this->assertFieldByName("formats[$format_id][weight]", 5, t('Text format weight was retained.'));
+ $this->assertFieldByName("formats[$format_id][weight]", 5, 'Text format weight was retained.');
// Disable text format.
$this->assertLinkByHref('admin/config/content/formats/' . $format_id . '/disable');
@@ -225,7 +240,7 @@ class FilterAdminTestCase extends DrupalWebTestCase {
// Verify that disabled text format no longer exists.
$this->drupalGet('admin/config/content/formats/' . $format_id);
- $this->assertResponse(404, t('Disabled text format no longer exists.'));
+ $this->assertResponse(404, 'Disabled text format no longer exists.');
// Attempt to create a format of the same machine name as the disabled
// format but with a different human readable name.
@@ -249,7 +264,7 @@ class FilterAdminTestCase extends DrupalWebTestCase {
}
/**
- * Test filter administration functionality.
+ * Tests filter administration functionality.
*/
function testFilterAdmin() {
// URL filter.
@@ -262,44 +277,44 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$plain = 'plain_text';
// Check that the fallback format exists and cannot be disabled.
- $this->assertTrue($plain == filter_fallback_format(), t('The fallback format is set to plain text.'));
+ $this->assertTrue($plain == filter_fallback_format(), 'The fallback format is set to plain text.');
$this->drupalGet('admin/config/content/formats');
- $this->assertNoRaw('admin/config/content/formats/' . $plain . '/disable', t('Disable link for the fallback format not found.'));
+ $this->assertNoRaw('admin/config/content/formats/' . $plain . '/disable', 'Disable link for the fallback format not found.');
$this->drupalGet('admin/config/content/formats/' . $plain . '/disable');
- $this->assertResponse(403, t('The fallback format cannot be disabled.'));
+ $this->assertResponse(403, 'The fallback format cannot be disabled.');
// Verify access permissions to Full HTML format.
- $this->assertTrue(filter_access(filter_format_load($full), $this->admin_user), t('Admin user may use Full HTML.'));
- $this->assertFalse(filter_access(filter_format_load($full), $this->web_user), t('Web user may not use Full HTML.'));
+ $this->assertTrue(filter_access(filter_format_load($full), $this->admin_user), 'Admin user may use Full HTML.');
+ $this->assertFalse(filter_access(filter_format_load($full), $this->web_user), 'Web user may not use Full HTML.');
// Add an additional tag.
$edit = array();
$edit['filters[filter_html][settings][allowed_html]'] = ' -
-
-
';
$this->drupalPost('admin/config/content/formats/' . $filtered, $edit, t('Save configuration'));
- $this->assertFieldByName('filters[filter_html][settings][allowed_html]', $edit['filters[filter_html][settings][allowed_html]'], t('Allowed HTML tag added.'));
+ $this->assertFieldByName('filters[filter_html][settings][allowed_html]', $edit['filters[filter_html][settings][allowed_html]'], 'Allowed HTML tag added.');
$result = db_query('SELECT * FROM {cache_filter}')->fetchObject();
- $this->assertFalse($result, t('Cache cleared.'));
+ $this->assertFalse($result, 'Cache cleared.');
$elements = $this->xpath('//select[@name=:first]/following::select[@name=:second]', array(
':first' => 'filters[' . $first_filter . '][weight]',
':second' => 'filters[' . $second_filter . '][weight]',
));
- $this->assertTrue(!empty($elements), t('Order confirmed in admin interface.'));
+ $this->assertTrue(!empty($elements), 'Order confirmed in admin interface.');
// Reorder filters.
$edit = array();
$edit['filters[' . $second_filter . '][weight]'] = 1;
$edit['filters[' . $first_filter . '][weight]'] = 2;
$this->drupalPost(NULL, $edit, t('Save configuration'));
- $this->assertFieldByName('filters[' . $second_filter . '][weight]', 1, t('Order saved successfully.'));
- $this->assertFieldByName('filters[' . $first_filter . '][weight]', 2, t('Order saved successfully.'));
+ $this->assertFieldByName('filters[' . $second_filter . '][weight]', 1, 'Order saved successfully.');
+ $this->assertFieldByName('filters[' . $first_filter . '][weight]', 2, 'Order saved successfully.');
$elements = $this->xpath('//select[@name=:first]/following::select[@name=:second]', array(
':first' => 'filters[' . $second_filter . '][weight]',
':second' => 'filters[' . $first_filter . '][weight]',
));
- $this->assertTrue(!empty($elements), t('Reorder confirmed in admin interface.'));
+ $this->assertTrue(!empty($elements), 'Reorder confirmed in admin interface.');
$result = db_query('SELECT * FROM {filter} WHERE format = :format ORDER BY weight ASC', array(':format' => $filtered));
$filters = array();
@@ -308,7 +323,7 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$filters[] = $filter;
}
}
- $this->assertTrue(($filters[0]->name == $second_filter && $filters[1]->name == $first_filter), t('Order confirmed in database.'));
+ $this->assertTrue(($filters[0]->name == $second_filter && $filters[1]->name == $first_filter), 'Order confirmed in database.');
// Add format.
$edit = array();
@@ -318,19 +333,19 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$edit['filters[' . $second_filter . '][status]'] = TRUE;
$edit['filters[' . $first_filter . '][status]'] = TRUE;
$this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
- $this->assertRaw(t('Added text format %format.', array('%format' => $edit['name'])), t('New filter created.'));
+ $this->assertRaw(t('Added text format %format.', array('%format' => $edit['name'])), 'New filter created.');
drupal_static_reset('filter_formats');
$format = filter_format_load($edit['format']);
- $this->assertNotNull($format, t('Format found in database.'));
+ $this->assertNotNull($format, 'Format found in database.');
- $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.'));
+ $this->assertFieldByName('roles[' . DRUPAL_AUTHENTICATED_RID . ']', '', 'Role found.');
+ $this->assertFieldByName('filters[' . $second_filter . '][status]', '', 'Line break filter found.');
+ $this->assertFieldByName('filters[' . $first_filter . '][status]', '', 'Url filter found.');
// Disable new filter.
$this->drupalPost('admin/config/content/formats/' . $format->format . '/disable', array(), t('Disable'));
- $this->assertRaw(t('Disabled text format %format.', array('%format' => $edit['name'])), t('Format successfully disabled.'));
+ $this->assertRaw(t('Disabled text format %format.', array('%format' => $edit['name'])), 'Format successfully disabled.');
// Allow authenticated users on full HTML.
$format = filter_format_load($full);
@@ -338,14 +353,14 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$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.'));
+ $this->assertRaw(t('The text format %format has been updated.', array('%format' => $format->name)), 'Full HTML format successfully updated.');
// Switch user.
$this->drupalLogout();
$this->drupalLogin($this->web_user);
$this->drupalGet('node/add/page');
- $this->assertRaw('', t('Full HTML filter accessible.'));
+ $this->assertRaw('', 'Full HTML filter accessible.');
// Use filtered HTML and see if it removes tags that are not allowed.
$body = '' . $this->randomName() . '';
@@ -358,20 +373,20 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$edit["body[$langcode][0][value]"] = $text;
$edit["body[$langcode][0][format]"] = $filtered;
$this->drupalPost('node/add/page', $edit, t('Save'));
- $this->assertRaw(t('Basic page %title has been created.', array('%title' => $edit["title"])), t('Filtered node created.'));
+ $this->assertRaw(t('Basic page %title has been created.', array('%title' => $edit["title"])), 'Filtered node created.');
$node = $this->drupalGetNodeByTitle($edit["title"]);
- $this->assertTrue($node, t('Node found in database.'));
+ $this->assertTrue($node, 'Node found in database.');
$this->drupalGet('node/' . $node->nid);
- $this->assertRaw($body . $extra_text, t('Filter removed invalid tag.'));
+ $this->assertRaw($body . $extra_text, 'Filter removed invalid tag.');
// Use plain text and see if it escapes all tags, whether allowed or not.
$edit = array();
$edit["body[$langcode][0][format]"] = $plain;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->drupalGet('node/' . $node->nid);
- $this->assertText(check_plain($text), t('The "Plain text" text format escapes all HTML tags.'));
+ $this->assertText(check_plain($text), 'The "Plain text" text format escapes all HTML tags.');
// Switch user.
$this->drupalLogout();
@@ -382,22 +397,22 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$edit = array();
$edit['filters[filter_html][settings][allowed_html]'] = ' -
-
- ';
$this->drupalPost('admin/config/content/formats/' . $filtered, $edit, t('Save configuration'));
- $this->assertFieldByName('filters[filter_html][settings][allowed_html]', $edit['filters[filter_html][settings][allowed_html]'], t('Changes reverted.'));
+ $this->assertFieldByName('filters[filter_html][settings][allowed_html]', $edit['filters[filter_html][settings][allowed_html]'], 'Changes reverted.');
// Full HTML.
$edit = array();
$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[' . DRUPAL_AUTHENTICATED_RID . ']', $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'], t('Changes reverted.'));
+ $this->assertRaw(t('The text format %format has been updated.', array('%format' => $format->name)), 'Full HTML format successfully reverted.');
+ $this->assertFieldByName('roles[' . DRUPAL_AUTHENTICATED_RID . ']', $edit['roles[' . DRUPAL_AUTHENTICATED_RID . ']'], 'Changes reverted.');
// Filter order.
$edit = array();
$edit['filters[' . $second_filter . '][weight]'] = 2;
$edit['filters[' . $first_filter . '][weight]'] = 1;
$this->drupalPost('admin/config/content/formats/' . $filtered, $edit, t('Save configuration'));
- $this->assertFieldByName('filters[' . $second_filter . '][weight]', $edit['filters[' . $second_filter . '][weight]'], t('Changes reverted.'));
- $this->assertFieldByName('filters[' . $first_filter . '][weight]', $edit['filters[' . $first_filter . '][weight]'], t('Changes reverted.'));
+ $this->assertFieldByName('filters[' . $second_filter . '][weight]', $edit['filters[' . $second_filter . '][weight]'], 'Changes reverted.');
+ $this->assertFieldByName('filters[' . $first_filter . '][weight]', $edit['filters[' . $first_filter . '][weight]'], 'Changes reverted.');
}
/**
@@ -413,11 +428,43 @@ class FilterAdminTestCase extends DrupalWebTestCase {
}
}
+/**
+ * Tests the filter format access functionality in the Filter module.
+ */
class FilterFormatAccessTestCase extends DrupalWebTestCase {
+ /**
+ * A user with administrative permissions.
+ *
+ * @var object
+ */
protected $admin_user;
+
+ /**
+ * A user with 'administer filters' permission.
+ *
+ * @var object
+ */
protected $filter_admin_user;
+
+ /**
+ * A user with permission to create and edit own content.
+ *
+ * @var object
+ */
protected $web_user;
+
+ /**
+ * An object representing an allowed text format.
+ *
+ * @var object
+ */
protected $allowed_format;
+
+ /**
+ * An object representing a disallowed text format.
+ *
+ * @var object
+ */
protected $disallowed_format;
public static function getInfo() {
@@ -471,23 +518,26 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
));
}
+ /**
+ * Tests the Filter format access permissions functionality.
+ */
function testFormatPermissions() {
// Make sure that a regular user only has access to the text format they
// were granted access to, as well to the fallback format.
- $this->assertTrue(filter_access($this->allowed_format, $this->web_user), t('A regular user has access to a text format they were granted access to.'));
- $this->assertFalse(filter_access($this->disallowed_format, $this->web_user), t('A regular user does not have access to a text format they were not granted access to.'));
- $this->assertTrue(filter_access(filter_format_load(filter_fallback_format()), $this->web_user), t('A regular user has access to the fallback format.'));
+ $this->assertTrue(filter_access($this->allowed_format, $this->web_user), 'A regular user has access to a text format they were granted access to.');
+ $this->assertFalse(filter_access($this->disallowed_format, $this->web_user), 'A regular user does not have access to a text format they were not granted access to.');
+ $this->assertTrue(filter_access(filter_format_load(filter_fallback_format()), $this->web_user), 'A regular user has access to the fallback format.');
// Perform similar checks as above, but now against the entire list of
// available formats for this user.
- $this->assertTrue(in_array($this->allowed_format->format, array_keys(filter_formats($this->web_user))), t('The allowed format appears in the list of available formats for a regular user.'));
- $this->assertFalse(in_array($this->disallowed_format->format, array_keys(filter_formats($this->web_user))), t('The disallowed format does not appear in the list of available formats for a regular user.'));
- $this->assertTrue(in_array(filter_fallback_format(), array_keys(filter_formats($this->web_user))), t('The fallback format appears in the list of available formats for a regular user.'));
+ $this->assertTrue(in_array($this->allowed_format->format, array_keys(filter_formats($this->web_user))), 'The allowed format appears in the list of available formats for a regular user.');
+ $this->assertFalse(in_array($this->disallowed_format->format, array_keys(filter_formats($this->web_user))), 'The disallowed format does not appear in the list of available formats for a regular user.');
+ $this->assertTrue(in_array(filter_fallback_format(), array_keys(filter_formats($this->web_user))), 'The fallback format appears in the list of available formats for a regular user.');
// Make sure that a regular user only has permission to use the format
// they were granted access to.
- $this->assertTrue(user_access(filter_permission_name($this->allowed_format), $this->web_user), t('A regular user has permission to use the allowed text format.'));
- $this->assertFalse(user_access(filter_permission_name($this->disallowed_format), $this->web_user), t('A regular user does not have permission to use the disallowed text format.'));
+ $this->assertTrue(user_access(filter_permission_name($this->allowed_format), $this->web_user), 'A regular user has permission to use the allowed text format.');
+ $this->assertFalse(user_access(filter_permission_name($this->disallowed_format), $this->web_user), 'A regular user does not have permission to use the disallowed text format.');
// Make sure that the allowed format appears on the node form and that
// the disallowed format does not.
@@ -502,11 +552,14 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
foreach ($elements as $element) {
$options[(string) $element['value']] = $element;
}
- $this->assertTrue(isset($options[$this->allowed_format->format]), t('The allowed text format appears as an option when adding a new node.'));
- $this->assertFalse(isset($options[$this->disallowed_format->format]), t('The disallowed text format does not appear as an option when adding a new node.'));
- $this->assertTrue(isset($options[filter_fallback_format()]), t('The fallback format appears as an option when adding a new node.'));
+ $this->assertTrue(isset($options[$this->allowed_format->format]), 'The allowed text format appears as an option when adding a new node.');
+ $this->assertFalse(isset($options[$this->disallowed_format->format]), 'The disallowed text format does not appear as an option when adding a new node.');
+ $this->assertTrue(isset($options[filter_fallback_format()]), 'The fallback format appears as an option when adding a new node.');
}
+ /**
+ * Tests if text format is available to a role.
+ */
function testFormatRoles() {
// Get the role ID assigned to the regular user; it must be the maximum.
$rid = max(array_keys($this->web_user->roles));
@@ -514,27 +567,27 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
// Check that this role appears in the list of roles that have access to an
// allowed text format, but does not appear in the list of roles that have
// access to a disallowed text format.
- $this->assertTrue(in_array($rid, array_keys(filter_get_roles_by_format($this->allowed_format))), t('A role which has access to a text format appears in the list of roles that have access to that format.'));
- $this->assertFalse(in_array($rid, array_keys(filter_get_roles_by_format($this->disallowed_format))), t('A role which does not have access to a text format does not appear in the list of roles that have access to that format.'));
+ $this->assertTrue(in_array($rid, array_keys(filter_get_roles_by_format($this->allowed_format))), 'A role which has access to a text format appears in the list of roles that have access to that format.');
+ $this->assertFalse(in_array($rid, array_keys(filter_get_roles_by_format($this->disallowed_format))), 'A role which does not have access to a text format does not appear in the list of roles that have access to that format.');
// Check that the correct text format appears in the list of formats
// available to that role.
- $this->assertTrue(in_array($this->allowed_format->format, array_keys(filter_get_formats_by_role($rid))), t('A text format which a role has access to appears in the list of formats available to that role.'));
- $this->assertFalse(in_array($this->disallowed_format->format, array_keys(filter_get_formats_by_role($rid))), t('A text format which a role does not have access to does not appear in the list of formats available to that role.'));
+ $this->assertTrue(in_array($this->allowed_format->format, array_keys(filter_get_formats_by_role($rid))), 'A text format which a role has access to appears in the list of formats available to that role.');
+ $this->assertFalse(in_array($this->disallowed_format->format, array_keys(filter_get_formats_by_role($rid))), 'A text format which a role does not have access to does not appear in the list of formats available to that role.');
// Check that the fallback format is always allowed.
- $this->assertEqual(filter_get_roles_by_format(filter_format_load(filter_fallback_format())), user_roles(), t('All roles have access to the fallback format.'));
- $this->assertTrue(in_array(filter_fallback_format(), array_keys(filter_get_formats_by_role($rid))), t('The fallback format appears in the list of allowed formats for any role.'));
+ $this->assertEqual(filter_get_roles_by_format(filter_format_load(filter_fallback_format())), user_roles(), 'All roles have access to the fallback format.');
+ $this->assertTrue(in_array(filter_fallback_format(), array_keys(filter_get_formats_by_role($rid))), 'The fallback format appears in the list of allowed formats for any role.');
}
/**
- * Test editing a page using a disallowed text format.
+ * Tests editing a page using a disallowed text format.
*
- * Verifies that regular users and administrators are able to edit a page,
- * but not allowed to change the fields which use an inaccessible text
- * format. Also verifies that fields which use a text format that does not
- * exist can be edited by administrators only, but that the administrator is
- * forced to choose a new format before saving the page.
+ * Verifies that regular users and administrators are able to edit a page, but
+ * not allowed to change the fields which use an inaccessible text format.
+ * Also verifies that fields which use a text format that does not exist can
+ * be edited by administrators only, but that the administrator is forced to
+ * choose a new format before saving the page.
*/
function testFormatWidgetPermissions() {
$langcode = LANGUAGE_NONE;
@@ -557,19 +610,19 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
$this->clickLink(t('Edit'));
// Verify that body field is read-only and contains replacement value.
- $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), t('Text format access denied message found.'));
+ $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
// Verify that title can be changed, but preview displays original body.
$new_edit = array();
$new_edit['title'] = $this->randomName(8);
$this->drupalPost(NULL, $new_edit, t('Preview'));
- $this->assertText($edit[$body_value_key], t('Old body found in preview.'));
+ $this->assertText($edit[$body_value_key], 'Old body found in preview.');
// Save and verify that only the title was changed.
$this->drupalPost(NULL, $new_edit, t('Save'));
- $this->assertNoText($edit['title'], t('Old title not found.'));
- $this->assertText($new_edit['title'], t('New title found.'));
- $this->assertText($edit[$body_value_key], t('Old body found.'));
+ $this->assertNoText($edit['title'], 'Old title not found.');
+ $this->assertText($new_edit['title'], 'New title found.');
+ $this->assertText($edit[$body_value_key], 'Old body found.');
// Check that even an administrator with "administer filters" permission
// cannot edit the body field if they do not have specific permission to
@@ -578,7 +631,7 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
// else.)
$this->drupalLogin($this->filter_admin_user);
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), t('Text format access denied message found.'));
+ $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
// Disable the text format used above.
filter_format_disable($this->disallowed_format);
@@ -589,14 +642,14 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
// edit content that does not have an assigned format.
$this->drupalLogin($this->web_user);
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), t('Text format access denied message found.'));
+ $this->assertFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", t('This field has been disabled because you do not have sufficient permissions to edit it.'), 'Text format access denied message found.');
// Log back in as the filter administrator and verify that the body field
// can be edited.
$this->drupalLogin($this->filter_admin_user);
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertNoFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", NULL, t('Text format access denied message not found.'));
- $this->assertFieldByXPath("//select[@name='$body_format_key']", NULL, t('Text format selector found.'));
+ $this->assertNoFieldByXPath("//textarea[@name='$body_value_key' and @disabled='disabled']", NULL, 'Text format access denied message not found.');
+ $this->assertFieldByXPath("//select[@name='$body_format_key']", NULL, 'Text format selector found.');
// Verify that trying to save the node without selecting a new text format
// produces an error message, and does not result in the node being saved.
@@ -604,17 +657,17 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
$new_title = $this->randomName(8);
$edit = array('title' => $new_title);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
- $this->assertText(t('!name field is required.', array('!name' => t('Text format'))), t('Error message is displayed.'));
+ $this->assertText(t('!name field is required.', array('!name' => t('Text format'))), 'Error message is displayed.');
$this->drupalGet('node/' . $node->nid);
- $this->assertText($old_title, t('Old title found.'));
- $this->assertNoText($new_title, t('New title not found.'));
+ $this->assertText($old_title, 'Old title found.');
+ $this->assertNoText($new_title, 'New title not found.');
// Now select a new text format and make sure the node can be saved.
$edit[$body_format_key] = filter_fallback_format();
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertUrl('node/' . $node->nid);
- $this->assertText($new_title, t('New title found.'));
- $this->assertNoText($old_title, t('Old title not found.'));
+ $this->assertText($new_title, 'New title found.');
+ $this->assertNoText($old_title, 'Old title not found.');
// Switch the text format to a new one, then disable that format and all
// other formats on the site (leaving only the fallback format).
@@ -638,19 +691,19 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
$new_title = $this->randomName(8);
$edit = array('title' => $new_title);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
- $this->assertText(t('!name field is required.', array('!name' => t('Text format'))), t('Error message is displayed.'));
+ $this->assertText(t('!name field is required.', array('!name' => t('Text format'))), 'Error message is displayed.');
$this->drupalGet('node/' . $node->nid);
- $this->assertText($old_title, t('Old title found.'));
- $this->assertNoText($new_title, t('New title not found.'));
+ $this->assertText($old_title, 'Old title found.');
+ $this->assertNoText($new_title, 'New title not found.');
$edit[$body_format_key] = filter_fallback_format();
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertUrl('node/' . $node->nid);
- $this->assertText($new_title, t('New title found.'));
- $this->assertNoText($old_title, t('Old title not found.'));
+ $this->assertText($new_title, 'New title found.');
+ $this->assertNoText($old_title, 'Old title not found.');
}
/**
- * Rebuild text format and permission caches in the thread running the tests.
+ * Rebuilds text format and permission caches in the thread running the tests.
*/
protected function resetFilterCaches() {
filter_formats_reset();
@@ -658,6 +711,9 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase {
}
}
+/**
+ * Tests the default filter functionality in the Filter module.
+ */
class FilterDefaultFormatTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -667,6 +723,9 @@ class FilterDefaultFormatTestCase extends DrupalWebTestCase {
);
}
+ /**
+ * Tests if the default text format is accessible to users.
+ */
function testDefaultTextFormats() {
// Create two text formats, and two users. The first user has access to
// both formats, but the second user only has access to the second one.
@@ -697,8 +756,8 @@ class FilterDefaultFormatTestCase extends DrupalWebTestCase {
// Check that each user's default format is the lowest weighted format that
// the user has access to.
- $this->assertEqual(filter_default_format($first_user), $first_format->format, t("The first user's default format is the lowest weighted format that the user has access to."));
- $this->assertEqual(filter_default_format($second_user), $second_format->format, t("The second user's default format is the lowest weighted format that the user has access to, and is different than the first user's."));
+ $this->assertEqual(filter_default_format($first_user), $first_format->format, "The first user's default format is the lowest weighted format that the user has access to.");
+ $this->assertEqual(filter_default_format($second_user), $second_format->format, "The second user's default format is the lowest weighted format that the user has access to, and is different than the first user's.");
// Reorder the two formats, and check that both users now have the same
// default.
@@ -706,11 +765,11 @@ class FilterDefaultFormatTestCase extends DrupalWebTestCase {
$edit['formats[' . $second_format->format . '][weight]'] = $minimum_weight - 3;
$this->drupalPost('admin/config/content/formats', $edit, t('Save changes'));
$this->resetFilterCaches();
- $this->assertEqual(filter_default_format($first_user), filter_default_format($second_user), t('After the formats are reordered, both users have the same default format.'));
+ $this->assertEqual(filter_default_format($first_user), filter_default_format($second_user), 'After the formats are reordered, both users have the same default format.');
}
/**
- * Rebuild text format and permission caches in the thread running the tests.
+ * Rebuilds text format and permission caches in the thread running the tests.
*/
protected function resetFilterCaches() {
filter_formats_reset();
@@ -718,6 +777,9 @@ class FilterDefaultFormatTestCase extends DrupalWebTestCase {
}
}
+/**
+ * Tests the behavior of check_markup() when it is called without text format.
+ */
class FilterNoFormatTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -727,6 +789,12 @@ class FilterNoFormatTestCase extends DrupalWebTestCase {
);
}
+ /**
+ * Tests text without format.
+ *
+ * Tests if text with no format is filtered the same way as text in the
+ * fallback format.
+ */
function testCheckMarkupNoFormat() {
// Create some text. Include some HTML and line breaks, so we get a good
// test of the filtering that is applied to it.
@@ -734,7 +802,7 @@ class FilterNoFormatTestCase extends DrupalWebTestCase {
// Make sure that when this text is run through check_markup() with no text
// format, it is filtered as though it is in the fallback format.
- $this->assertEqual(check_markup($text), check_markup($text, filter_fallback_format()), t('Text with no format is filtered the same as text in the fallback format.'));
+ $this->assertEqual(check_markup($text), check_markup($text, filter_fallback_format()), 'Text with no format is filtered the same as text in the fallback format.');
}
}
@@ -757,7 +825,10 @@ class FilterSecurityTestCase extends DrupalWebTestCase {
}
/**
- * Test that filtered content is emptied when an actively used filter module is disabled.
+ * Tests removal of filtered content when an active filter is disabled.
+ *
+ * Tests that filtered content is emptied when an actively used filter module
+ * is disabled.
*/
function testDisableFilterModule() {
// Create a new node.
@@ -765,7 +836,7 @@ class FilterSecurityTestCase extends DrupalWebTestCase {
$body_raw = $node->body[LANGUAGE_NONE][0]['value'];
$format_id = $node->body[LANGUAGE_NONE][0]['format'];
$this->drupalGet('node/' . $node->nid);
- $this->assertText($body_raw, t('Node body found.'));
+ $this->assertText($body_raw, 'Node body found.');
// Enable the filter_test_replace filter.
$edit = array(
@@ -775,15 +846,15 @@ class FilterSecurityTestCase extends DrupalWebTestCase {
// Verify that filter_test_replace filter replaced the content.
$this->drupalGet('node/' . $node->nid);
- $this->assertNoText($body_raw, t('Node body not found.'));
- $this->assertText('Filter: Testing filter', t('Testing filter output found.'));
+ $this->assertNoText($body_raw, 'Node body not found.');
+ $this->assertText('Filter: Testing filter', 'Testing filter output found.');
// Disable the text format entirely.
$this->drupalPost('admin/config/content/formats/' . $format_id . '/disable', array(), t('Disable'));
// Verify that the content is empty, because the text format does not exist.
$this->drupalGet('node/' . $node->nid);
- $this->assertNoText($body_raw, t('Node body not found.'));
+ $this->assertNoText($body_raw, 'Node body not found.');
}
}
@@ -800,7 +871,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
}
/**
- * Test the line break filter.
+ * Tests the line break filter.
*/
function testLineBreakFilter() {
// Setup dummy filter object.
@@ -870,7 +941,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
$limit = max(ini_get('pcre.backtrack_limit'), ini_get('pcre.recursion_limit'));
$source = $this->randomName($limit);
$result = _filter_autop($source);
- $success = $this->assertEqual($result, '
' . $source . "
\n", t('Line break filter can process very long strings.'));
+ $success = $this->assertEqual($result, '' . $source . "
\n", 'Line break filter can process very long strings.');
if (!$success) {
$this->verbose("\n" . $source . "\n
\n" . $result);
}
@@ -891,176 +962,176 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
function testFilterXSS() {
// Tag stripping, different ways to work around removal of HTML tags.
$f = filter_xss('');
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping -- simple script without special characters.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping -- simple script without special characters.');
$f = filter_xss('');
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping -- empty script with source.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping -- empty script with source.');
$f = filter_xss('');
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- non whitespace character after tag name.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- non whitespace character after tag name.');
$f = filter_xss('');
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- no space between tag and attribute.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- no space between tag and attribute.');
// Null between < and tag name works at least with IE6.
$f = filter_xss("<\0scr\0ipt>alert(0)");
- $this->assertNoNormalized($f, 'ipt', t('HTML tag stripping evasion -- breaking HTML with nulls.'));
+ $this->assertNoNormalized($f, 'ipt', 'HTML tag stripping evasion -- breaking HTML with nulls.');
$f = filter_xss("");
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- filter just removing "script".'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- filter just removing "script".');
$f = filter_xss('<');
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- double opening brackets.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- double opening brackets.');
$f = filter_xss('', array('img'));
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- a malformed image tag.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- a malformed image tag.');
$f = filter_xss('', array('blockquote'));
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- script in a blockqoute.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script in a blockqoute.');
$f = filter_xss("");
- $this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- script within a comment.'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script within a comment.');
// Dangerous attributes removal.
$f = filter_xss('', array('p'));
- $this->assertNoNormalized($f, 'onmouseover', t('HTML filter attributes removal -- events, no evasion.'));
+ $this->assertNoNormalized($f, 'onmouseover', 'HTML filter attributes removal -- events, no evasion.');
$f = filter_xss('
- ', array('li'));
- $this->assertNoNormalized($f, 'style', t('HTML filter attributes removal -- style, no evasion.'));
+ $this->assertNoNormalized($f, 'style', 'HTML filter attributes removal -- style, no evasion.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'onerror', t('HTML filter attributes removal evasion -- spaces before equals sign.'));
+ $this->assertNoNormalized($f, 'onerror', 'HTML filter attributes removal evasion -- spaces before equals sign.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'onabort', t('HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.'));
+ $this->assertNoNormalized($f, 'onabort', 'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'onmediaerror', t('HTML filter attributes removal evasion -- varying case.'));
+ $this->assertNoNormalized($f, 'onmediaerror', 'HTML filter attributes removal evasion -- varying case.');
// Works at least with IE6.
$f = filter_xss("
", array('img'));
- $this->assertNoNormalized($f, 'focus', t('HTML filter attributes removal evasion -- breaking with nulls.'));
+ $this->assertNoNormalized($f, 'focus', 'HTML filter attributes removal evasion -- breaking with nulls.');
// Only whitelisted scheme names allowed in attributes.
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing -- no evasion.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- no evasion.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- no quotes.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no quotes.');
// A bit like CVE-2006-0070.
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- no alert ;)'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no alert ;)');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- grave accents.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- grave accents.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing -- rare attribute.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- rare attribute.');
$f = filter_xss('', array('table'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing -- another tag.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- another tag.');
$f = filter_xss(' ', array('base'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing -- one more attribute and tag.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- one more attribute and tag.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- varying case.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- varying case.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- UTF-8 decimal encoding.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 decimal encoding.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- long UTF-8 encoding.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- long UTF-8 encoding.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- UTF-8 hex encoding.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 hex encoding.');
$f = filter_xss("
", array('img'));
- $this->assertNoNormalized($f, 'script', t('HTML scheme clearing evasion -- an embedded tab.'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an embedded tab.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'script', t('HTML scheme clearing evasion -- an encoded, embedded tab.'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded tab.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'script', t('HTML scheme clearing evasion -- an encoded, embedded newline.'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded newline.');
// With
this test would fail, but the entity gets turned into
// 
, so it's OK.
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'script', t('HTML scheme clearing evasion -- an encoded, embedded carriage return.'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded carriage return.');
$f = filter_xss("
", array('img'));
- $this->assertNoNormalized($f, 'cript', t('HTML scheme clearing evasion -- broken into many lines.'));
+ $this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- broken into many lines.');
$f = filter_xss("
", array('img'));
- $this->assertNoNormalized($f, 'cript', t('HTML scheme clearing evasion -- embedded nulls.'));
+ $this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- embedded nulls.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'javascript', t('HTML scheme clearing evasion -- spaces and metacharacters before scheme.'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'vbscript', t('HTML scheme clearing evasion -- another scheme.'));
+ $this->assertNoNormalized($f, 'vbscript', 'HTML scheme clearing evasion -- another scheme.');
$f = filter_xss('
', array('img'));
- $this->assertNoNormalized($f, 'nosuchscheme', t('HTML scheme clearing evasion -- unknown scheme.'));
+ $this->assertNoNormalized($f, 'nosuchscheme', 'HTML scheme clearing evasion -- unknown scheme.');
// Netscape 4.x javascript entities.
$f = filter_xss('
', array('br'));
- $this->assertNoNormalized($f, 'alert', t('Netscape 4.x javascript entities.'));
+ $this->assertNoNormalized($f, 'alert', 'Netscape 4.x javascript entities.');
// DRUPAL-SA-2008-006: Invalid UTF-8, these only work as reflected XSS with
// Internet Explorer 6.
$f = filter_xss("\" style=\"background-image: url(javascript:alert(0));\"\xe0
", array('p'));
- $this->assertNoNormalized($f, 'style', t('HTML filter -- invalid UTF-8.'));
+ $this->assertNoNormalized($f, 'style', 'HTML filter -- invalid UTF-8.');
$f = filter_xss("\xc0aaa");
- $this->assertEqual($f, '', t('HTML filter -- overlong UTF-8 sequences.'));
+ $this->assertEqual($f, '', 'HTML filter -- overlong UTF-8 sequences.');
$f = filter_xss("Who's Online");
- $this->assertNormalized($f, "who's online", t('HTML filter -- html entity number'));
+ $this->assertNormalized($f, "who's online", 'HTML filter -- html entity number');
$f = filter_xss("Who's Online");
- $this->assertNormalized($f, "who's online", t('HTML filter -- encoded html entity number'));
+ $this->assertNormalized($f, "who's online", 'HTML filter -- encoded html entity number');
$f = filter_xss("Who&#039; Online");
- $this->assertNormalized($f, "who' online", t('HTML filter -- double encoded html entity number'));
+ $this->assertNormalized($f, "who' online", 'HTML filter -- double encoded html entity number');
}
/**
- * Test filter settings, defaults, access restrictions and similar.
+ * Tests filter settings, defaults, access restrictions and similar.
*
* @todo This is for functions like filter_filter and check_markup, whose
* functionality is not completely focused on filtering. Some ideas:
@@ -1085,38 +1156,38 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
// HTML filter is not able to secure some tags, these should never be
// allowed.
$f = _filter_html('', $filter);
- $this->assertNoNormalized($f, 'script', t('HTML filter should always remove script tags.'));
+ $this->assertNoNormalized($f, 'script', 'HTML filter should always remove script tags.');
$f = _filter_html('', $filter);
- $this->assertNoNormalized($f, 'iframe', t('HTML filter should always remove iframe tags.'));
+ $this->assertNoNormalized($f, 'iframe', 'HTML filter should always remove iframe tags.');
$f = _filter_html('', $filter);
- $this->assertNoNormalized($f, 'object', t('HTML filter should always remove object tags.'));
+ $this->assertNoNormalized($f, 'object', 'HTML filter should always remove object tags.');
$f = _filter_html('', $filter);
- $this->assertNoNormalized($f, 'style', t('HTML filter should always remove style tags.'));
+ $this->assertNoNormalized($f, 'style', 'HTML filter should always remove style tags.');
// Some tags make CSRF attacks easier, let the user take the risk herself.
$f = _filter_html('
', $filter);
- $this->assertNoNormalized($f, 'img', t('HTML filter should remove img tags on default.'));
+ $this->assertNoNormalized($f, 'img', 'HTML filter should remove img tags on default.');
$f = _filter_html('', $filter);
- $this->assertNoNormalized($f, 'img', t('HTML filter should remove input tags on default.'));
+ $this->assertNoNormalized($f, 'img', 'HTML filter should remove input tags on default.');
// Filtering content of some attributes is infeasible, these shouldn't be
// allowed too.
$f = _filter_html('
', $filter);
- $this->assertNoNormalized($f, 'style', t('HTML filter should remove style attribute on default.'));
+ $this->assertNoNormalized($f, 'style', 'HTML filter should remove style attribute on default.');
$f = _filter_html('', $filter);
- $this->assertNoNormalized($f, 'onerror', t('HTML filter should remove on* attributes on default.'));
+ $this->assertNoNormalized($f, 'onerror', 'HTML filter should remove on* attributes on default.');
$f = _filter_html('
', $filter);
- $this->assertNoNormalized($f, 'onerror', t('HTML filter should remove empty on* attributes on default.'));
+ $this->assertNoNormalized($f, 'onerror', 'HTML filter should remove empty on* attributes on default.');
}
/**
- * Test the spam deterrent.
+ * Tests the spam deterrent.
*/
function testNoFollowFilter() {
// Setup dummy filter object.
@@ -1130,35 +1201,35 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
// Test if the rel="nofollow" attribute is added, even if we try to prevent
// it.
$f = _filter_html('text', $filter);
- $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent -- no evasion.'));
+ $this->assertNormalized($f, 'rel="nofollow"', 'Spam deterrent -- no evasion.');
$f = _filter_html('text', $filter);
- $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- capital A.'));
+ $this->assertNormalized($f, 'rel="nofollow"', 'Spam deterrent evasion -- capital A.');
$f = _filter_html("text", $filter);
- $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- non whitespace character after tag name.'));
+ $this->assertNormalized($f, 'rel="nofollow"', 'Spam deterrent evasion -- non whitespace character after tag name.');
$f = _filter_html("<\0a\0 href=\"http://www.example.com/\">text", $filter);
- $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- some nulls.'));
+ $this->assertNormalized($f, 'rel="nofollow"', 'Spam deterrent evasion -- some nulls.');
$f = _filter_html('text', $filter);
- $this->assertNoNormalized($f, 'rel="follow"', t('Spam deterrent evasion -- with rel set - rel="follow" removed.'));
- $this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- with rel set - rel="nofollow" added.'));
+ $this->assertNoNormalized($f, 'rel="follow"', 'Spam deterrent evasion -- with rel set - rel="follow" removed.');
+ $this->assertNormalized($f, 'rel="nofollow"', 'Spam deterrent evasion -- with rel set - rel="nofollow" added.');
}
/**
- * Test the loose, admin HTML filter.
+ * Tests the loose, admin HTML filter.
*/
function testFilterXSSAdmin() {
// DRUPAL-SA-2008-044
$f = filter_xss_admin('');
- $this->assertNoNormalized($f, 'object', t('Admin HTML filter -- should not allow object tag.'));
+ $this->assertNoNormalized($f, 'object', 'Admin HTML filter -- should not allow object tag.');
$f = filter_xss_admin('');
- $this->assertNoNormalized($f, 'script', t('Admin HTML filter -- should not allow script tag.'));
+ $this->assertNoNormalized($f, 'script', 'Admin HTML filter -- should not allow script tag.');
$f = filter_xss_admin(' ');
- $this->assertEqual($f, '', t('Admin HTML filter -- should never allow some tags.'));
+ $this->assertEqual($f, '', 'Admin HTML filter -- should never allow some tags.');
}
/**
@@ -1487,13 +1558,13 @@ www.example.com with a newline in comments -->
foreach ($tasks as $value => $is_expected) {
// Not using assertIdentical, since combination with strpos() is hard to grok.
if ($is_expected) {
- $success = $this->assertTrue(strpos($result, $value) !== FALSE, t('@source: @value found.', array(
+ $success = $this->assertTrue(strpos($result, $value) !== FALSE, format_string('@source: @value found.', array(
'@source' => var_export($source, TRUE),
'@value' => var_export($value, TRUE),
)));
}
else {
- $success = $this->assertTrue(strpos($result, $value) === FALSE, t('@source: @value not found.', array(
+ $success = $this->assertTrue(strpos($result, $value) === FALSE, format_string('@source: @value not found.', array(
'@source' => var_export($source, TRUE),
'@value' => var_export($value, TRUE),
)));
@@ -1541,121 +1612,121 @@ www.example.com with a newline in comments -->
}
/**
- * Test the HTML corrector filter.
+ * Tests the HTML corrector filter.
*
* @todo This test could really use some validity checking function.
*/
function testHtmlCorrectorFilter() {
// Tag closing.
$f = _filter_htmlcorrector('text');
- $this->assertEqual($f, '
text
', t('HTML corrector -- tag closing at the end of input.'));
+ $this->assertEqual($f, 'text
', 'HTML corrector -- tag closing at the end of input.');
$f = _filter_htmlcorrector('text
text');
- $this->assertEqual($f, '
text
text
', t('HTML corrector -- tag closing.'));
+ $this->assertEqual($f, 'text
text
', 'HTML corrector -- tag closing.');
$f = _filter_htmlcorrector("- e1
- e2");
- $this->assertEqual($f, "
- e1
- e2
", t('HTML corrector -- unclosed list tags.'));
+ $this->assertEqual($f, "- e1
- e2
", 'HTML corrector -- unclosed list tags.');
$f = _filter_htmlcorrector('content');
- $this->assertEqual($f, 'content', t('HTML corrector -- unclosed tag with attribute.'));
+ $this->assertEqual($f, 'content', 'HTML corrector -- unclosed tag with attribute.');
// XHTML slash for empty elements.
$f = _filter_htmlcorrector('
');
- $this->assertEqual($f, '
', t('HTML corrector -- XHTML closing slash.'));
+ $this->assertEqual($f, '
', 'HTML corrector -- XHTML closing slash.');
$f = _filter_htmlcorrector('test
');
- $this->assertEqual($f, 'test
', t('HTML corrector -- Convert uppercased tags to proper lowercased ones.'));
+ $this->assertEqual($f, 'test
', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.');
$f = _filter_htmlcorrector('test
');
- $this->assertEqual($f, 'test
', t('HTML corrector -- Convert uppercased tags to proper lowercased ones.'));
+ $this->assertEqual($f, 'test
', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.');
$f = _filter_htmlcorrector('test
');
- $this->assertEqual($f, 'test
', t('HTML corrector -- Let proper XHTML pass through.'));
+ $this->assertEqual($f, 'test
', 'HTML corrector -- Let proper XHTML pass through.');
$f = _filter_htmlcorrector('test
');
- $this->assertEqual($f, 'test
', t('HTML corrector -- Let proper XHTML pass through, but ensure there is a single space before the closing slash.'));
+ $this->assertEqual($f, 'test
', 'HTML corrector -- Let proper XHTML pass through, but ensure there is a single space before the closing slash.');
$f = _filter_htmlcorrector('test
');
- $this->assertEqual($f, 'test
', t('HTML corrector -- Let proper XHTML pass through, but ensure there are not too many spaces before the closing slash.'));
+ $this->assertEqual($f, 'test
', 'HTML corrector -- Let proper XHTML pass through, but ensure there are not too many spaces before the closing slash.');
$f = _filter_htmlcorrector('');
- $this->assertEqual($f, '', t('HTML corrector -- Convert XHTML that is properly formed but that would not be compatible with typical HTML user agents.'));
+ $this->assertEqual($f, '', 'HTML corrector -- Convert XHTML that is properly formed but that would not be compatible with typical HTML user agents.');
$f = _filter_htmlcorrector('test1
test2');
- $this->assertEqual($f, 'test1
test2', t('HTML corrector -- Automatically close single tags.'));
+ $this->assertEqual($f, 'test1
test2', 'HTML corrector -- Automatically close single tags.');
$f = _filter_htmlcorrector('line1
line2');
- $this->assertEqual($f, 'line1
line2', t('HTML corrector -- Automatically close single tags.'));
+ $this->assertEqual($f, 'line1
line2', 'HTML corrector -- Automatically close single tags.');
$f = _filter_htmlcorrector('line1
line2');
- $this->assertEqual($f, 'line1
line2', t('HTML corrector -- Automatically close single tags.'));
+ $this->assertEqual($f, 'line1
line2', 'HTML corrector -- Automatically close single tags.');
$f = _filter_htmlcorrector('
test');
- $this->assertEqual($f, '
test', t('HTML corrector -- Automatically close single tags.'));
+ $this->assertEqual($f, '
test', 'HTML corrector -- Automatically close single tags.');
$f = _filter_htmlcorrector('
');
- $this->assertEqual($f, '
', t("HTML corrector -- Transform empty tags to a single closed tag if the tag's content model is EMPTY."));
+ $this->assertEqual($f, '
', "HTML corrector -- Transform empty tags to a single closed tag if the tag's content model is EMPTY.");
$f = _filter_htmlcorrector('');
- $this->assertEqual($f, '', t("HTML corrector -- Do not transform empty tags to a single closed tag if the tag's content model is not EMPTY."));
+ $this->assertEqual($f, '', "HTML corrector -- Do not transform empty tags to a single closed tag if the tag's content model is not EMPTY.");
$f = _filter_htmlcorrector('line1
line2');
- $this->assertEqual($f, 'line1
line2', t('HTML corrector -- Move non-inline elements outside of inline containers.'));
+ $this->assertEqual($f, 'line1
line2', 'HTML corrector -- Move non-inline elements outside of inline containers.');
$f = _filter_htmlcorrector('line1
line2');
- $this->assertEqual($f, 'line1
line2', t('HTML corrector -- Move non-inline elements outside of inline containers.'));
+ $this->assertEqual($f, 'line1
line2', 'HTML corrector -- Move non-inline elements outside of inline containers.');
$f = _filter_htmlcorrector('test
test
\n');
- $this->assertEqual($f, 'test
test
\n', t('HTML corrector -- Auto-close improperly nested tags.'));
+ $this->assertEqual($f, 'test
test
\n', 'HTML corrector -- Auto-close improperly nested tags.');
$f = _filter_htmlcorrector('Line1
bold stuff');
- $this->assertEqual($f, 'Line1
bold stuff
', t('HTML corrector -- Properly close unclosed tags, and remove useless closing tags.'));
+ $this->assertEqual($f, 'Line1
bold stuff
', 'HTML corrector -- Properly close unclosed tags, and remove useless closing tags.');
$f = _filter_htmlcorrector('test ');
- $this->assertEqual($f, 'test ', t('HTML corrector -- Do not touch HTML comments.'));
+ $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.');
$f = _filter_htmlcorrector('test ');
- $this->assertEqual($f, 'test ', t('HTML corrector -- Do not touch HTML comments.'));
+ $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.');
$f = _filter_htmlcorrector('test ');
$this->assertEqual($f, 'test ', t('HTML corrector -- Do not touch HTML comments.'));
+ comment
-->', 'HTML corrector -- Do not touch HTML comments.');
$f = _filter_htmlcorrector('test ');
- $this->assertEqual($f, 'test ', t('HTML corrector -- Do not touch HTML comments.'));
+ $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.');
$f = _filter_htmlcorrector('test ');
- $this->assertEqual($f, 'test ', t('HTML corrector -- Do not touch HTML comments.'));
+ $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.');
$f = _filter_htmlcorrector('test\n
\n');
- $this->assertEqual($f, 'test\n
\n', t('HTML corrector -- New-lines are accepted and kept as-is.'));
+ $this->assertEqual($f, 'test\n
\n', 'HTML corrector -- New-lines are accepted and kept as-is.');
$f = _filter_htmlcorrector('دروبال');
- $this->assertEqual($f, '
دروبال
', t('HTML corrector -- Encoding is correctly kept.'));
+ $this->assertEqual($f, 'دروبال
', 'HTML corrector -- Encoding is correctly kept.');
$f = _filter_htmlcorrector('');
$this->assertEqual($f, '', t('HTML corrector -- CDATA added to script element'));
+', 'HTML corrector -- CDATA added to script element');
$f = _filter_htmlcorrector('');
$this->assertEqual($f, '', t('HTML corrector -- CDATA added to a nested script element'));
+', 'HTML corrector -- CDATA added to a nested script element');
$f = _filter_htmlcorrector('');
$this->assertEqual($f, '', t('HTML corrector -- CDATA added to a style element.'));
+', 'HTML corrector -- CDATA added to a style element.');
$filtered_data = _filter_htmlcorrector('',
- t('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '/* '/*',
- t('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '
',
- t('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '
',
- t('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '// '// 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.'));
+ $this->assertRaw(t('Added text format %format.', array('%format' => $name)), 'New format created.');
+ $this->assertText('hook_filter_format_insert invoked.', 'hook_filter_format_insert was invoked.');
$format_id = $edit['format'];
@@ -1818,8 +1892,8 @@ class FilterHooksTestCase extends DrupalWebTestCase {
$edit = array();
$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.'));
+ $this->assertRaw(t('The text format %format has been updated.', array('%format' => $name)), 'Format successfully updated.');
+ $this->assertText('hook_filter_format_update invoked.', 'hook_filter_format_update() was invoked.');
// Add a new custom block.
$custom_block = array();
@@ -1829,16 +1903,16 @@ class FilterHooksTestCase extends DrupalWebTestCase {
// Use the format created.
$custom_block['body[format]'] = $format_id;
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
- $this->assertText(t('The block has been created.'), t('New block successfully created.'));
+ $this->assertText(t('The block has been created.'), 'New block successfully created.');
// Verify the new block is in the database.
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
- $this->assertNotNull($bid, t('New block found in database'));
+ $this->assertNotNull($bid, 'New block found in database');
// Disable the text format.
$this->drupalPost('admin/config/content/formats/' . $format_id . '/disable', array(), t('Disable'));
- $this->assertRaw(t('Disabled text format %format.', array('%format' => $name)), t('Format successfully disabled.'));
- $this->assertText('hook_filter_format_disable invoked.', t('hook_filter_format_disable() was invoked.'));
+ $this->assertRaw(t('Disabled text format %format.', array('%format' => $name)), 'Format successfully disabled.');
+ $this->assertText('hook_filter_format_disable invoked.', 'hook_filter_format_disable() was invoked.');
}
}
@@ -1846,6 +1920,11 @@ class FilterHooksTestCase extends DrupalWebTestCase {
* Tests filter settings.
*/
class FilterSettingsTestCase extends DrupalWebTestCase {
+ /**
+ * The installation profile to use with this test class.
+ *
+ * @var string
+ */
protected $profile = 'testing';
public static function getInfo() {
diff --git a/modules/forum/forum-rtl.css b/modules/forum/forum-rtl.css
index b475e42..3f2a88b 100644
--- a/modules/forum/forum-rtl.css
+++ b/modules/forum/forum-rtl.css
@@ -7,6 +7,10 @@
float: right;
margin: 0 0 0 9px;
}
+#forum div.indent {
+ margin-left: 0;
+ margin-right: 20px;
+}
.forum-topic-navigation {
padding: 1em 3em 0 0;
}
diff --git a/modules/forum/forum.css b/modules/forum/forum.css
index a758bc6..480e07b 100644
--- a/modules/forum/forum.css
+++ b/modules/forum/forum.css
@@ -29,7 +29,7 @@
}
#forum div.indent {
- margin-left: 20px;
+ margin-left: 20px; /* LTR */
}
#forum .icon div {
background-image: url(../../misc/forum-icons.png);
diff --git a/modules/forum/forum.info b/modules/forum/forum.info
index b18a0e5..b7d518c 100644
--- a/modules/forum/forum.info
+++ b/modules/forum/forum.info
@@ -8,3 +8,9 @@ core = 7.x
files[] = forum.test
configure = admin/structure/forum
stylesheets[all][] = forum.css
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index fe0ef79..1224418 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -233,6 +233,8 @@ function forum_entity_info_alter(&$info) {
}
/**
+ * Implements callback_entity_info_uri().
+ *
* Entity URI callback used in forum_entity_info_alter().
*/
function forum_uri($forum) {
@@ -261,10 +263,10 @@ function _forum_node_check_node_type($node) {
* Implements hook_node_view().
*/
function forum_node_view($node, $view_mode) {
- $vid = variable_get('forum_nav_vocabulary', 0);
- $vocabulary = taxonomy_vocabulary_load($vid);
if (_forum_node_check_node_type($node)) {
if ($view_mode == 'full' && node_is_page($node)) {
+ $vid = variable_get('forum_nav_vocabulary', 0);
+ $vocabulary = taxonomy_vocabulary_load($vid);
// Breadcrumb navigation
$breadcrumb[] = l(t('Home'), NULL);
$breadcrumb[] = l($vocabulary->name, 'forum');
@@ -658,7 +660,12 @@ function forum_block_info() {
* Implements hook_block_configure().
*/
function forum_block_configure($delta = '') {
- $form['forum_block_num_' . $delta] = array('#type' => 'select', '#title' => t('Number of topics'), '#default_value' => variable_get('forum_block_num_' . $delta, '5'), '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)));
+ $form['forum_block_num_' . $delta] = array(
+ '#type' => 'select',
+ '#title' => t('Number of topics'),
+ '#default_value' => variable_get('forum_block_num_' . $delta, '5'),
+ '#options' => drupal_map_assoc(range(2, 20))
+ );
return $form;
}
diff --git a/modules/forum/forum.test b/modules/forum/forum.test
index 6937c62..bc68a3e 100644
--- a/modules/forum/forum.test
+++ b/modules/forum/forum.test
@@ -94,18 +94,18 @@ class ForumTestCase extends DrupalWebTestCase {
$edit = array();
$edit['modules[Core][forum][enable]'] = FALSE;
$this->drupalPost('admin/modules', $edit, t('Save configuration'));
- $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
+ $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
module_list(TRUE);
- $this->assertFalse(module_exists('forum'), t('Forum module is not enabled.'));
+ $this->assertFalse(module_exists('forum'), 'Forum module is not enabled.');
// 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';
$this->drupalPost('admin/modules', $edit, t('Save configuration'));
- $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
+ $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
module_list(TRUE);
- $this->assertTrue(module_exists('forum'), t('Forum module is enabled.'));
+ $this->assertTrue(module_exists('forum'), 'Forum module is enabled.');
}
/**
@@ -166,17 +166,17 @@ class ForumTestCase extends DrupalWebTestCase {
$xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="topics"]', $forum_arg);
$topics = $this->xpath($xpath);
$topics = trim($topics[0]);
- $this->assertEqual($topics, '6', t('Number of topics found.'));
+ $this->assertEqual($topics, '6', 'Number of topics found.');
// Verify the number of unread topics.
$unread_topics = _forum_topics_unread($this->forum['tid'], $this->edit_any_topics_user->uid);
$unread_topics = format_plural($unread_topics, '1 new', '@count new');
$xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="topics"]//a', $forum_arg);
- $this->assertFieldByXPath($xpath, $unread_topics, t('Number of unread topics found.'));
+ $this->assertFieldByXPath($xpath, $unread_topics, 'Number of unread topics found.');
// Verify total number of posts in forum.
$xpath = $this->buildXPathQuery('//tr[@id=:forum]//td[@class="posts"]', $forum_arg);
- $this->assertFieldByXPath($xpath, '6', t('Number of posts found.'));
+ $this->assertFieldByXPath($xpath, '6', 'Number of posts found.');
// Test loading multiple forum nodes on the front page.
$this->drupalLogin($this->drupalCreateUser(array('administer content types', 'create forum content')));
@@ -226,7 +226,7 @@ class ForumTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/forum', array('title' => $this->randomName(10), 'body[' . LANGUAGE_NONE .'][0][value]' => $this->randomName(120)), t('Save'));
$nid_count = db_query('SELECT COUNT(nid) FROM {node}')->fetchField();
- $this->assertEqual(0, $nid_count, t('A forum node was not created when missing a forum vocabulary.'));
+ $this->assertEqual(0, $nid_count, 'A forum node was not created when missing a forum vocabulary.');
// Reset the defaults for future tests.
module_enable(array('forum'));
@@ -247,14 +247,14 @@ class ForumTestCase extends DrupalWebTestCase {
$edit['blocks[forum_active][region]'] = 'sidebar_second';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertResponse(200);
- $this->assertText(t('The block settings have been updated.'), t('Active forum topics forum block was enabled'));
+ $this->assertText(t('The block settings have been updated.'), 'Active forum topics forum block was enabled');
// Enable the new forum block.
$edit = array();
$edit['blocks[forum_new][region]'] = 'sidebar_second';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertResponse(200);
- $this->assertText(t('The block settings have been updated.'), t('[New forum topics] Forum block was enabled'));
+ $this->assertText(t('The block settings have been updated.'), '[New forum topics] Forum block was enabled');
// Retrieve forum menu id.
$mlid = db_query_range("SELECT mlid FROM {menu_links} WHERE link_path = 'forum' AND menu_name = 'navigation' AND module = 'system' ORDER BY mlid ASC", 0, 1)->fetchField();
@@ -272,13 +272,13 @@ class ForumTestCase extends DrupalWebTestCase {
// Verify "edit container" link exists and functions correctly.
$this->drupalGet('admin/structure/forum');
$this->clickLink('edit container');
- $this->assertRaw('Edit container', t('Followed the link to edit the container'));
+ $this->assertRaw('Edit container', 'Followed the link to edit the container');
// Create forum inside the forum container.
$this->forum = $this->createForum('forum', $this->container['tid']);
// Verify the "edit forum" link exists and functions correctly.
$this->drupalGet('admin/structure/forum');
$this->clickLink('edit forum');
- $this->assertRaw('Edit forum', t('Followed the link to edit the forum'));
+ $this->assertRaw('Edit forum', 'Followed the link to edit the forum');
// Navigate back to forum structure page.
$this->drupalGet('admin/structure/forum');
// Create second forum in container.
@@ -334,15 +334,15 @@ class ForumTestCase extends DrupalWebTestCase {
// Edit the vocabulary.
$this->drupalPost('admin/structure/taxonomy/' . $original_settings->machine_name . '/edit', $edit, t('Save'));
$this->assertResponse(200);
- $this->assertRaw(t('Updated vocabulary %name.', array('%name' => $title)), t('Vocabulary was edited'));
+ $this->assertRaw(t('Updated vocabulary %name.', array('%name' => $title)), 'Vocabulary was edited');
// Grab the newly edited vocabulary.
entity_get_controller('taxonomy_vocabulary')->resetCache();
$current_settings = taxonomy_vocabulary_load($vid);
// Make sure we actually edited the vocabulary properly.
- $this->assertEqual($current_settings->name, $title, t('The name was updated'));
- $this->assertEqual($current_settings->description, $description, t('The description was updated'));
+ $this->assertEqual($current_settings->name, $title, 'The name was updated');
+ $this->assertEqual($current_settings->description, $description, 'The description was updated');
// Restore the original vocabulary.
taxonomy_vocabulary_save($original_settings);
@@ -379,7 +379,7 @@ class ForumTestCase extends DrupalWebTestCase {
$this->drupalPost('admin/structure/forum/add/' . $type, $edit, t('Save'));
$this->assertResponse(200);
$type = ($type == 'container') ? 'forum container' : 'forum';
- $this->assertRaw(t('Created new @type %term.', array('%term' => $name, '@type' => t($type))), t(ucfirst($type) . ' was created'));
+ $this->assertRaw(t('Created new @type %term.', array('%term' => $name, '@type' => t($type))), format_string('@type was created', array('@type' => ucfirst($type))));
// Verify forum.
$term = db_query("SELECT * FROM {taxonomy_term_data} t WHERE t.vid = :vid AND t.name = :name AND t.description = :desc", array(':vid' => variable_get('forum_nav_vocabulary', ''), ':name' => $name, ':desc' => $description))->fetchAssoc();
@@ -461,24 +461,24 @@ class ForumTestCase extends DrupalWebTestCase {
$type = t('Forum topic');
if ($container) {
- $this->assertNoRaw(t('@type %title has been created.', array('@type' => $type, '%title' => $title)), t('Forum topic was not created'));
- $this->assertRaw(t('The item %title is a forum container, not a forum.', array('%title' => $forum['name'])), t('Error message was shown'));
+ $this->assertNoRaw(t('@type %title has been created.', array('@type' => $type, '%title' => $title)), 'Forum topic was not created');
+ $this->assertRaw(t('The item %title is a forum container, not a forum.', array('%title' => $forum['name'])), 'Error message was shown');
return;
}
else {
- $this->assertRaw(t('@type %title has been created.', array('@type' => $type, '%title' => $title)), t('Forum topic was created'));
- $this->assertNoRaw(t('The item %title is a forum container, not a forum.', array('%title' => $forum['name'])), t('No error message was shown'));
+ $this->assertRaw(t('@type %title has been created.', array('@type' => $type, '%title' => $title)), 'Forum topic was created');
+ $this->assertNoRaw(t('The item %title is a forum container, not a forum.', array('%title' => $forum['name'])), 'No error message was shown');
}
// Retrieve node object, ensure that the topic was created and in the proper forum.
$node = $this->drupalGetNodeByTitle($title);
- $this->assertTrue($node != NULL, t('Node @title was loaded', array('@title' => $title)));
+ $this->assertTrue($node != NULL, format_string('Node @title was loaded', array('@title' => $title)));
$this->assertEqual($node->taxonomy_forums[LANGUAGE_NONE][0]['tid'], $tid, 'Saved forum topic was in the expected forum');
// View forum topic.
$this->drupalGet('node/' . $node->nid);
- $this->assertRaw($title, t('Subject was found'));
- $this->assertRaw($body, t('Body was found'));
+ $this->assertRaw($title, 'Subject was found');
+ $this->assertRaw($body, 'Body was found');
return $node;
}
@@ -502,14 +502,14 @@ class ForumTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/help/forum');
$this->assertResponse($response2);
if ($response2 == 200) {
- $this->assertTitle(t('Forum | Drupal'), t('Forum help title was displayed'));
- $this->assertText(t('Forum'), t('Forum help node was displayed'));
+ $this->assertTitle(t('Forum | Drupal'), 'Forum help title was displayed');
+ $this->assertText(t('Forum'), 'Forum help node was displayed');
}
// Verify the forum blocks were displayed.
$this->drupalGet('');
$this->assertResponse(200);
- $this->assertText(t('New forum topics'), t('[New forum topics] Forum block was displayed'));
+ $this->assertText(t('New forum topics'), '[New forum topics] Forum block was displayed');
// View forum container page.
$this->verifyForumView($this->container);
@@ -521,20 +521,20 @@ class ForumTestCase extends DrupalWebTestCase {
// View forum node.
$this->drupalGet('node/' . $node->nid);
$this->assertResponse(200);
- $this->assertTitle($node->title . ' | Drupal', t('Forum node was displayed'));
+ $this->assertTitle($node->title . ' | Drupal', 'Forum node was displayed');
$breadcrumb = array(
l(t('Home'), NULL),
l(t('Forums'), 'forum'),
l($this->container['name'], 'forum/' . $this->container['tid']),
l($this->forum['name'], 'forum/' . $this->forum['tid']),
);
- $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), t('Breadcrumbs were displayed'));
+ $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), 'Breadcrumbs were displayed');
// View forum edit node.
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertTitle('Edit Forum topic ' . $node->title . ' | Drupal', t('Forum edit node was displayed'));
+ $this->assertTitle('Edit Forum topic ' . $node->title . ' | Drupal', 'Forum edit node was displayed');
}
if ($response == 200) {
@@ -547,7 +547,7 @@ class ForumTestCase extends DrupalWebTestCase {
$edit["taxonomy_forums[$langcode]"] = $this->root_forum['tid'];
$edit['shadow'] = TRUE;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
- $this->assertRaw(t('Forum topic %title has been updated.', array('%title' => $edit["title"])), t('Forum node was edited'));
+ $this->assertRaw(t('Forum topic %title has been updated.', array('%title' => $edit["title"])), 'Forum node was edited');
// Verify topic was moved to a different forum.
$forum_tid = db_query("SELECT tid FROM {forum} WHERE nid = :nid AND vid = :vid", array(
@@ -559,7 +559,7 @@ class ForumTestCase extends DrupalWebTestCase {
// Delete forum node.
$this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
$this->assertResponse($response);
- $this->assertRaw(t('Forum topic %title has been deleted.', array('%title' => $edit['title'])), t('Forum node was deleted'));
+ $this->assertRaw(t('Forum topic %title has been deleted.', array('%title' => $edit['title'])), 'Forum node was deleted');
}
}
@@ -575,7 +575,7 @@ class ForumTestCase extends DrupalWebTestCase {
// View forum page.
$this->drupalGet('forum/' . $forum['tid']);
$this->assertResponse(200);
- $this->assertTitle($forum['name'] . ' | Drupal', t('Forum name was displayed'));
+ $this->assertTitle($forum['name'] . ' | Drupal', 'Forum name was displayed');
$breadcrumb = array(
l(t('Home'), NULL),
@@ -585,7 +585,7 @@ class ForumTestCase extends DrupalWebTestCase {
$breadcrumb[] = l($parent['name'], 'forum/' . $parent['tid']);
}
- $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), t('Breadcrumbs were displayed'));
+ $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), 'Breadcrumbs were displayed');
}
/**
@@ -677,6 +677,7 @@ class ForumIndexTestCase extends DrupalWebTestCase {
'status' => FALSE,
);
$this->drupalPost("node/{$node->nid}/edit", $edit, t('Save'));
+ $this->drupalGet("node/{$node->nid}");
$this->assertText(t('Access denied'), 'Unpublished node is no longer accessible.');
// Verify that the node no longer appears on the index.
diff --git a/modules/help/help.info b/modules/help/help.info
index d4c1b36..8e4b239 100644
--- a/modules/help/help.info
+++ b/modules/help/help.info
@@ -4,3 +4,9 @@ package = Core
version = VERSION
core = 7.x
files[] = help.test
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/help/help.test b/modules/help/help.test
index a18e68c..da12ccc 100644
--- a/modules/help/help.test
+++ b/modules/help/help.test
@@ -52,17 +52,17 @@ class HelpTestCase extends DrupalWebTestCase {
// Check for css on admin/help.
$this->drupalLogin($this->big_user);
$this->drupalGet('admin/help');
- $this->assertRaw(drupal_get_path('module', 'help') . '/help.css', t('The help.css file is present in the HTML.'));
+ $this->assertRaw(drupal_get_path('module', 'help') . '/help.css', 'The help.css file is present in the HTML.');
// Verify that introductory help text exists, goes for 100% module coverage.
$this->assertRaw(t('For more information, refer to the specific topics listed in the next section or to the online Drupal handbooks.', array('@drupal' => 'http://drupal.org/documentation')), 'Help intro text correctly appears.');
// Verify that help topics text appears.
- $this->assertRaw('' . t('Help topics') . '
' . t('Help is available on the following items:') . '
', t('Help topics text correctly appears.'));
+ $this->assertRaw('' . t('Help topics') . '
' . t('Help is available on the following items:') . '
', 'Help topics text correctly appears.');
// Make sure links are properly added for modules implementing hook_help().
foreach ($this->modules as $module => $name) {
- $this->assertLink($name, 0, t('Link properly added to @name (admin/help/@module)', array('@module' => $module, '@name' => $name)));
+ $this->assertLink($name, 0, format_string('Link properly added to @name (admin/help/@module)', array('@module' => $module, '@name' => $name)));
}
}
@@ -78,8 +78,8 @@ class HelpTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/help/' . $module);
$this->assertResponse($response);
if ($response == 200) {
- $this->assertTitle($name . ' | Drupal', t('[' . $module . '] Title was displayed'));
- $this->assertRaw('' . t($name) . '
', t('[' . $module . '] Heading was displayed'));
+ $this->assertTitle($name . ' | Drupal', format_string('%module title was displayed', array('%module' => $module)));
+ $this->assertRaw('' . t($name) . '
', format_string('%module heading was displayed', array('%module' => $module)));
}
}
}
@@ -132,6 +132,6 @@ class NoHelpTestCase extends DrupalWebTestCase {
$this->drupalLogin($this->big_user);
$this->drupalGet('admin/help');
- $this->assertNoText('Hook menu tests', t('Making sure the test module menu_test does not display a help link in admin/help'));
+ $this->assertNoText('Hook menu tests', 'Making sure the test module menu_test does not display a help link in admin/help');
}
}
diff --git a/modules/image/image.admin.inc b/modules/image/image.admin.inc
index ab99a49..cebe894 100644
--- a/modules/image/image.admin.inc
+++ b/modules/image/image.admin.inc
@@ -32,10 +32,9 @@ function image_style_list() {
* An image style array.
* @ingroup forms
* @see image_style_form_submit()
- * @see image_style_name_validate()
*/
function image_style_form($form, &$form_state, $style) {
- $title = t('Edit %name style', array('%name' => $style['name']));
+ $title = t('Edit %name style', array('%name' => $style['label']));
drupal_set_title($title, PASS_THROUGH);
// Adjust this form for styles that must be overridden to edit.
@@ -56,27 +55,31 @@ function image_style_form($form, &$form_state, $style) {
'#markup' => theme('image_style_preview', array('style' => $style)),
);
+ // Show the Image Style label.
+ $form['label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Image style name'),
+ '#default_value' => $style['label'],
+ '#disabled' => !$editable,
+ '#required' => TRUE,
+ );
+
// Allow the name of the style to be changed, unless this style is
// provided by a module's hook_default_image_styles().
- if ($style['storage'] & IMAGE_STORAGE_MODULE) {
- $form['name'] = array(
- '#type' => 'item',
- '#title' => t('Image style name'),
- '#markup' => $style['name'],
- '#description' => t('This image style is being provided by %module module and may not be renamed.', array('%module' => $style['module'])),
- );
- }
- else {
- $form['name'] = array(
- '#type' => 'textfield',
- '#size' => '64',
- '#title' => t('Image style name'),
- '#default_value' => $style['name'],
- '#description' => t('The name is used in URLs for generated images. Use only lowercase alphanumeric characters, underscores (_), and hyphens (-).'),
- '#element_validate' => array('image_style_name_validate'),
- '#required' => TRUE,
- );
- }
+ $form['name'] = array(
+ '#type' => 'machine_name',
+ '#size' => '64',
+ '#default_value' => $style['name'],
+ '#disabled' => !$editable,
+ '#description' => t('The name is used in URLs for generated images. Use only lowercase alphanumeric characters, underscores (_), and hyphens (-).'),
+ '#required' => TRUE,
+ '#machine_name' => array(
+ 'exists' => 'image_style_load',
+ 'source' => array('label'),
+ 'replace_pattern' => '[^0-9a-z_\-]',
+ 'error' => t('Please only use lowercase alphanumeric characters, underscores (_), and hyphens (-) for style names.'),
+ ),
+ );
// Build the list of existing image effects for this image style.
$form['effects'] = array(
@@ -199,7 +202,7 @@ function image_style_form_add_submit($form, &$form_state) {
* Submit handler for overriding a module-defined style.
*/
function image_style_form_override_submit($form, &$form_state) {
- drupal_set_message(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $form_state['image_style']['name'])));
+ drupal_set_message(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $form_state['image_style']['label'])));
image_default_style_save($form_state['image_style']);
}
@@ -207,11 +210,10 @@ function image_style_form_override_submit($form, &$form_state) {
* Submit handler for saving an image style.
*/
function image_style_form_submit($form, &$form_state) {
- // Update the image style name if it has changed.
+ // Update the image style.
$style = $form_state['image_style'];
- if (isset($form_state['values']['name']) && $style['name'] != $form_state['values']['name']) {
- $style['name'] = $form_state['values']['name'];
- }
+ $style['name'] = $form_state['values']['name'];
+ $style['label'] = $form_state['values']['label'];
// Update image effect weights.
if (!empty($form_state['values']['effects'])) {
@@ -236,18 +238,26 @@ function image_style_form_submit($form, &$form_state) {
*
* @ingroup forms
* @see image_style_add_form_submit()
- * @see image_style_name_validate()
*/
function image_style_add_form($form, &$form_state) {
- $form['name'] = array(
+ $form['label'] = array(
'#type' => 'textfield',
- '#size' => '64',
'#title' => t('Style name'),
'#default_value' => '',
- '#description' => t('The name is used in URLs for generated images. Use only lowercase alphanumeric characters, underscores (_), and hyphens (-).'),
- '#element_validate' => array('image_style_name_validate'),
'#required' => TRUE,
);
+ $form['name'] = array(
+ '#type' => 'machine_name',
+ '#description' => t('The name is used in URLs for generated images. Use only lowercase alphanumeric characters, underscores (_), and hyphens (-).'),
+ '#size' => '64',
+ '#required' => TRUE,
+ '#machine_name' => array(
+ 'exists' => 'image_style_load',
+ 'source' => array('label'),
+ 'replace_pattern' => '[^0-9a-z_\-]',
+ 'error' => t('Please only use lowercase alphanumeric characters, underscores (_), and hyphens (-) for style names.'),
+ ),
+ );
$form['submit'] = array(
'#type' => 'submit',
@@ -261,14 +271,22 @@ function image_style_add_form($form, &$form_state) {
* Submit handler for adding a new image style.
*/
function image_style_add_form_submit($form, &$form_state) {
- $style = array('name' => $form_state['values']['name']);
+ $style = array(
+ 'name' => $form_state['values']['name'],
+ 'label' => $form_state['values']['label'],
+ );
$style = image_style_save($style);
- drupal_set_message(t('Style %name was created.', array('%name' => $style['name'])));
+ drupal_set_message(t('Style %name was created.', array('%name' => $style['label'])));
$form_state['redirect'] = 'admin/config/media/image-styles/edit/' . $style['name'];
}
/**
* Element validate function to ensure unique, URL safe style names.
+ *
+ * This function is no longer used in Drupal core since image style names are
+ * now validated using #machine_name functionality. It is kept for backwards
+ * compatibility (since non-core modules may be using it) and will be removed
+ * in Drupal 8.
*/
function image_style_name_validate($element, $form_state) {
// Check for duplicates.
@@ -295,7 +313,7 @@ function image_style_name_validate($element, $form_state) {
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'] => ''));
+ $replacement_styles = array_diff_key(image_style_options(TRUE, PASS_THROUGH), array($style['name'] => ''));
$form['replacement'] = array(
'#title' => t('Replacement style'),
'#type' => 'select',
@@ -305,7 +323,7 @@ function image_style_delete_form($form, &$form_state, $style) {
return confirm_form(
$form,
- t('Optionally select a style before deleting %style', array('%style' => $style['name'])),
+ t('Optionally select a style before deleting %style', array('%style' => $style['label'])),
'admin/config/media/image-styles',
t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted.'),
t('Delete'), t('Cancel')
@@ -319,19 +337,19 @@ function image_style_delete_form_submit($form, &$form_state) {
$style = $form_state['image_style'];
image_style_delete($style, $form_state['values']['replacement']);
- drupal_set_message(t('Style %name was deleted.', array('%name' => $style['name'])));
+ drupal_set_message(t('Style %name was deleted.', array('%name' => $style['label'])));
$form_state['redirect'] = 'admin/config/media/image-styles';
}
/**
* Confirmation form to revert a database style to its default.
*/
-function image_style_revert_form($form, $form_state, $style) {
+function image_style_revert_form($form, &$form_state, $style) {
$form_state['image_style'] = $style;
return confirm_form(
$form,
- t('Revert the %style style?', array('%style' => $style['name'])),
+ t('Revert the %style style?', array('%style' => $style['label'])),
'admin/config/media/image-styles',
t('Reverting this style will delete the customized settings and restore the defaults provided by the @module module.', array('@module' => $style['module'])),
t('Revert'), t('Cancel')
@@ -342,7 +360,7 @@ function image_style_revert_form($form, $form_state, $style) {
* Submit handler to convert an overridden style to its default.
*/
function image_style_revert_form_submit($form, &$form_state) {
- drupal_set_message(t('The %style style has been reverted to its defaults.', array('%style' => $form_state['image_style']['name'])));
+ drupal_set_message(t('The %style style has been reverted to its defaults.', array('%style' => $form_state['image_style']['label'])));
image_default_style_revert($form_state['image_style']);
$form_state['redirect'] = 'admin/config/media/image-styles';
}
@@ -439,7 +457,7 @@ function image_effect_delete_form($form, &$form_state, $style, $effect) {
$form_state['image_style'] = $style;
$form_state['image_effect'] = $effect;
- $question = t('Are you sure you want to delete the @effect effect from the %style style?', array('%style' => $style['name'], '@effect' => $effect['label']));
+ $question = t('Are you sure you want to delete the @effect effect from the %style style?', array('%style' => $style['label'], '@effect' => $effect['label']));
return confirm_form($form, $question, 'admin/config/media/image-styles/edit/' . $style['name'], '', t('Delete'));
}
@@ -574,15 +592,15 @@ function image_crop_form($data) {
'#type' => 'radios',
'#title' => t('Anchor'),
'#options' => array(
- 'left-top' => t('Top') . ' ' . t('Left'),
- 'center-top' => t('Top') . ' ' . t('Center'),
- 'right-top' => t('Top') . ' ' . t('Right'),
- 'left-center' => t('Center') . ' ' . t('Left'),
+ 'left-top' => t('Top left'),
+ 'center-top' => t('Top center'),
+ 'right-top' => t('Top right'),
+ 'left-center' => t('Center left'),
'center-center' => t('Center'),
- 'right-center' => t('Center') . ' ' . t('Right'),
- 'left-bottom' => t('Bottom') . ' ' . t('Left'),
- 'center-bottom' => t('Bottom') . ' ' . t('Center'),
- 'right-bottom' => t('Bottom') . ' ' . t('Right'),
+ 'right-center' => t('Center right'),
+ 'left-bottom' => t('Bottom left'),
+ 'center-bottom' => t('Bottom center'),
+ 'right-bottom' => t('Bottom right'),
),
'#theme' => 'image_anchor',
'#default_value' => $data['anchor'],
@@ -650,7 +668,7 @@ function theme_image_style_list($variables) {
$rows = array();
foreach ($styles as $style) {
$row = array();
- $row[] = l($style['name'], 'admin/config/media/image-styles/edit/' . $style['name']);
+ $row[] = l($style['label'], 'admin/config/media/image-styles/edit/' . $style['name']);
$link_attributes = array(
'attributes' => array(
'class' => array('image-style-link'),
@@ -805,7 +823,7 @@ function theme_image_style_preview($variables) {
// Build the preview of the image style.
$preview_url = file_create_url($preview_file) . '?cache_bypass=' . REQUEST_TIME;
$output .= '';
- $output .= check_plain($style['name']) . ' (' . l(t('view actual size'), file_create_url($preview_file) . '?' . time()) . ')';
+ $output .= check_plain($style['label']) . ' (' . l(t('view actual size'), file_create_url($preview_file) . '?' . time()) . ')';
$output .= '';
$output .= '' . theme('image', array('path' => $preview_url, 'alt' => t('Sample modified image'), 'title' => '', 'attributes' => $preview_attributes)) . '';
$output .= '' . $preview_image['height'] . 'px';
diff --git a/modules/image/image.api.php b/modules/image/image.api.php
index 1cb2b0d..8115116 100644
--- a/modules/image/image.api.php
+++ b/modules/image/image.api.php
@@ -177,6 +177,7 @@ function hook_image_default_styles() {
$styles = array();
$styles['mymodule_preview'] = array(
+ 'label' => 'My module preview',
'effects' => array(
array(
'name' => 'image_scale',
diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc
index 60c0f5a..6d1867c 100644
--- a/modules/image/image.field.inc
+++ b/modules/image/image.field.inc
@@ -311,7 +311,7 @@ function image_field_widget_settings_form($field, $instance) {
$form['preview_image_style'] = array(
'#title' => t('Preview image style'),
'#type' => 'select',
- '#options' => image_style_options(FALSE),
+ '#options' => image_style_options(FALSE, PASS_THROUGH),
'#empty_option' => '<' . t('no preview') . '>',
'#default_value' => $settings['preview_image_style'],
'#description' => t('The preview image will be shown while editing the content.'),
@@ -351,7 +351,7 @@ function image_field_widget_form(&$form, &$form_state, $field, $instance, $langc
if ($field['cardinality'] == 1) {
// If there's only one field, return it as delta 0.
if (empty($elements[0]['#default_value']['fid'])) {
- $elements[0]['#description'] = theme('file_upload_help', array('description' => $instance['description'], 'upload_validators' => $elements[0]['#upload_validators']));
+ $elements[0]['#description'] = theme('file_upload_help', array('description' => field_filter_xss($instance['description']), 'upload_validators' => $elements[0]['#upload_validators']));
}
}
else {
@@ -495,7 +495,7 @@ function image_field_formatter_settings_form($field, $instance, $view_mode, $for
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
- $image_styles = image_style_options(FALSE);
+ $image_styles = image_style_options(FALSE, PASS_THROUGH);
$element['image_style'] = array(
'#title' => t('Image style'),
'#type' => 'select',
@@ -528,7 +528,7 @@ function image_field_formatter_settings_summary($field, $instance, $view_mode) {
$summary = array();
- $image_styles = image_style_options(FALSE);
+ $image_styles = image_style_options(FALSE, PASS_THROUGH);
// Unset possible 'No defined styles' option.
unset($image_styles['']);
// Styles could be lost because of enabled/disabled modules that defines
diff --git a/modules/image/image.info b/modules/image/image.info
index d7eece1..1e17ac1 100644
--- a/modules/image/image.info
+++ b/modules/image/image.info
@@ -6,3 +6,9 @@ core = 7.x
dependencies[] = file
files[] = image.test
configure = admin/config/media/image-styles
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/image/image.install b/modules/image/image.install
index b7aac71..45bcbbb 100644
--- a/modules/image/image.install
+++ b/modules/image/image.install
@@ -41,11 +41,18 @@ function image_schema() {
'not null' => TRUE,
),
'name' => array(
- 'description' => 'The style name.',
+ 'description' => 'The style machine name.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
),
+ 'label' => array(
+ 'description' => 'The style administrative name.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
),
'primary key' => array('isid'),
'unique keys' => array(
@@ -391,7 +398,8 @@ function image_update_7002(array &$sandbox) {
}
// Process the table at the top of the list.
- $table = reset(array_keys($sandbox['tables']));
+ $keys = array_keys($sandbox['tables']);
+ $table = reset($keys);
$sandbox['processed'] += _image_update_7002_populate_dimensions($table, $sandbox['tables'][$table], $sandbox['last_fid']);
// Has the table been fully processed?
@@ -447,6 +455,30 @@ function image_update_7004() {
}
}
+/**
+ * Add a column to the 'image_style' table to store administrative labels.
+ */
+function image_update_7005() {
+ $field = array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The style administrative name.',
+ );
+ db_add_field('image_styles', 'label', $field);
+
+ // Do a direct query here, rather than calling image_styles(),
+ // in case Image module is disabled.
+ $styles = db_query('SELECT name FROM {image_styles}')->fetchCol();
+ foreach ($styles as $style) {
+ db_update('image_styles')
+ ->fields(array('label' => $style))
+ ->condition('name', $style)
+ ->execute();
+ }
+}
+
/**
* @} End of "addtogroup updates-7.x-extra".
*/
diff --git a/modules/image/image.module b/modules/image/image.module
index d7178ad..fac8de9 100644
--- a/modules/image/image.module
+++ b/modules/image/image.module
@@ -254,7 +254,7 @@ function image_form_system_file_system_settings_alter(&$form, &$form_state) {
}
/**
- * Submit handler for the file system settings form.
+ * Form submission handler for system_file_system_settings().
*
* Adds a menu rebuild after the public file path has been changed, so that the
* menu router item depending on that file path will be regenerated.
@@ -312,9 +312,9 @@ function image_file_download($uri) {
return -1;
}
- // Private file access for the original files. Note that we only
- // check access for non-temporary images, since file.module will
- // grant access for all temporary files.
+ // Private file access for the original files. Note that we only check access
+ // for non-temporary images, since file.module will grant access for all
+ // temporary files.
$files = file_load_multiple(array(), array('uri' => $uri));
if (count($files)) {
$file = reset($files);
@@ -347,6 +347,7 @@ function image_image_default_styles() {
$styles = array();
$styles['thumbnail'] = array(
+ 'label' => 'Thumbnail (100x100)',
'effects' => array(
array(
'name' => 'image_scale',
@@ -357,6 +358,7 @@ function image_image_default_styles() {
);
$styles['medium'] = array(
+ 'label' => 'Medium (220x220)',
'effects' => array(
array(
'name' => 'image_scale',
@@ -367,6 +369,7 @@ function image_image_default_styles() {
);
$styles['large'] = array(
+ 'label' => 'Large (480x480)',
'effects' => array(
array(
'name' => 'image_scale',
@@ -537,7 +540,7 @@ function image_field_update_instance($instance, $prior_instance) {
}
/**
- * Clear cached versions of a specific file in all styles.
+ * Clears cached versions of a specific file in all styles.
*
* @param $path
* The Drupal file path to the original image.
@@ -553,7 +556,7 @@ function image_path_flush($path) {
}
/**
- * Get an array of all styles and their settings.
+ * Gets an array of all styles and their settings.
*
* @return
* An array of styles keyed by the image style ID (isid).
@@ -575,6 +578,7 @@ function image_styles() {
$module_styles = module_invoke($module, 'image_default_styles');
foreach ($module_styles as $style_name => $style) {
$style['name'] = $style_name;
+ $style['label'] = empty($style['label']) ? $style_name : $style['label'];
$style['module'] = $module;
$style['storage'] = IMAGE_STORAGE_DEFAULT;
foreach ($style['effects'] as $key => $effect) {
@@ -614,7 +618,9 @@ function image_styles() {
}
/**
- * Load a style by style name or ID. May be used as a loader for menu items.
+ * Loads a style by style name or ID.
+ *
+ * May be used as a loader for menu items.
*
* @param $name
* The name of the style.
@@ -623,6 +629,7 @@ function image_styles() {
* @param $include
* If set, this loader will restrict to a specific type of image style, may be
* one of the defined Image style storage constants.
+ *
* @return
* An image style array containing the following keys:
* - "isid": The unique image style ID.
@@ -660,12 +667,20 @@ function image_style_load($name = NULL, $isid = NULL, $include = NULL) {
}
/**
- * Save an image style.
+ * Saves an image style.
*
- * @param style
- * An image style array.
- * @return
- * An image style array. In the case of a new style, 'isid' will be populated.
+ * @param array $style
+ * An image style array containing:
+ * - name: A unique name for the style.
+ * - isid: (optional) An image style ID.
+ *
+ * @return array
+ * An image style array containing:
+ * - name: An unique name for the style.
+ * - old_name: The original name for the style.
+ * - isid: An image style ID.
+ * - is_new: TRUE if this is a new style, and FALSE if it is an existing
+ * style.
*/
function image_style_save($style) {
if (isset($style['isid']) && is_numeric($style['isid'])) {
@@ -678,6 +693,10 @@ function image_style_save($style) {
}
}
else {
+ // Add a default label when not given.
+ if (empty($style['label'])) {
+ $style['label'] = $style['name'];
+ }
drupal_write_record('image_styles', $style);
$style['is_new'] = TRUE;
}
@@ -692,13 +711,14 @@ function image_style_save($style) {
}
/**
- * Delete an image style.
+ * Deletes an image style.
*
* @param $style
* An image style array.
* @param $replacement_style_name
* (optional) When deleting a style, specify a replacement style name so
* that existing settings (if any) may be converted to a new style.
+ *
* @return
* TRUE on success.
*/
@@ -717,14 +737,17 @@ function image_style_delete($style, $replacement_style_name = '') {
}
/**
- * Load all the effects for an image style.
+ * Loads all the effects for an image style.
*
- * @param $style
- * An image style array.
- * @return
+ * @param array $style
+ * An image style array containing:
+ * - isid: The unique image style ID that contains this image effect.
+ *
+ * @return array
* An array of image effects associated with specified image style in the
* format array('isid' => array()), or an empty array if the specified style
* has no effects.
+ * @see image_effects()
*/
function image_style_effects($style) {
$effects = image_effects();
@@ -739,23 +762,32 @@ function image_style_effects($style) {
}
/**
- * Get an array of image styles suitable for using as select list options.
+ * Gets an array of image styles suitable for using as select list options.
*
* @param $include_empty
* If TRUE a option will be inserted in the options array.
+ * @param $output
+ * Optional flag determining how the options will be sanitized on output.
+ * Leave this at the default (CHECK_PLAIN) if you are using the output of
+ * this function directly in an HTML context, such as for checkbox or radio
+ * button labels, and do not plan to sanitize it on your own. If using the
+ * output of this function as select list options (its primary use case), you
+ * should instead set this flag to PASS_THROUGH to avoid double-escaping of
+ * the output (the form API sanitizes select list options by default).
+ *
* @return
- * Array of image styles both key and value are set to style name.
+ * Array of image styles with the machine name as key and the label as value.
*/
-function image_style_options($include_empty = TRUE) {
+function image_style_options($include_empty = TRUE, $output = CHECK_PLAIN) {
$styles = image_styles();
$options = array();
if ($include_empty && !empty($styles)) {
$options[''] = t('');
}
- // Use the array concatenation operator '+' here instead of array_merge(),
- // because the latter loses the datatype of the array keys, turning
- // associative string keys into numeric ones without warning.
- $options = $options + drupal_map_assoc(array_keys($styles));
+ foreach ($styles as $name => $style) {
+ $options[$name] = ($output == PASS_THROUGH) ? $style['label'] : check_plain($style['label']);
+ }
+
if (empty($options)) {
$options[''] = t('No defined styles');
}
@@ -763,7 +795,7 @@ function image_style_options($include_empty = TRUE) {
}
/**
- * Menu callback; Given a style and image path, generate a derivative.
+ * Page callback: Generates a derivative, given a style and image path.
*
* After generating an image, transfer it to the requesting agent.
*
@@ -780,9 +812,11 @@ function image_style_deliver($style, $scheme) {
// derivative token is valid. (Sites which require image derivatives to be
// generated without a token can set the 'image_allow_insecure_derivatives'
// variable to TRUE to bypass the latter check, but this will increase the
- // site's vulnerability to denial-of-service attacks.)
+ // site's vulnerability to denial-of-service attacks. To prevent this
+ // variable from leaving the site vulnerable to the most serious attacks, a
+ // token is always required when a derivative of a derivative is requested.)
$valid = !empty($style) && file_stream_wrapper_valid_scheme($scheme);
- if (!variable_get('image_allow_insecure_derivatives', FALSE)) {
+ if (!variable_get('image_allow_insecure_derivatives', FALSE) || strpos(ltrim($target, '\/'), 'styles/') === 0) {
$valid = $valid && isset($_GET[IMAGE_DERIVATIVE_TOKEN]) && $_GET[IMAGE_DERIVATIVE_TOKEN] === image_style_path_token($style['name'], $scheme . '://' . $target);
}
if (!$valid) {
@@ -801,7 +835,7 @@ function image_style_deliver($style, $scheme) {
else {
$headers = module_invoke_all('file_download', $image_uri);
if (in_array(-1, $headers) || empty($headers)) {
- return drupal_access_denied();
+ return MENU_ACCESS_DENIED;
}
if (count($headers)) {
foreach ($headers as $name => $value) {
@@ -811,6 +845,12 @@ function image_style_deliver($style, $scheme) {
}
}
+ // Confirm that the original source image exists before trying to process it.
+ if (!is_file($image_uri)) {
+ watchdog('image', 'Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', array('%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri));
+ return MENU_NOT_FOUND;
+ }
+
// Don't start generating the image if the derivative already exists or if
// generation is in progress in another thread.
$lock_name = 'image_style_deliver:' . $style['name'] . ':' . drupal_hash_base64($image_uri);
@@ -820,6 +860,7 @@ function image_style_deliver($style, $scheme) {
// Tell client to retry again in 3 seconds. Currently no browsers are known
// to support Retry-After.
drupal_add_http_header('Status', '503 Service Unavailable');
+ drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
drupal_add_http_header('Retry-After', 3);
print t('Image generation in progress. Try again shortly.');
drupal_exit();
@@ -841,6 +882,7 @@ function image_style_deliver($style, $scheme) {
else {
watchdog('image', 'Unable to generate the derived image located at %path.', array('%path' => $derivative_uri));
drupal_add_http_header('Status', '500 Internal Server Error');
+ drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
print t('Error generating image.');
drupal_exit();
}
@@ -867,6 +909,11 @@ function image_style_deliver($style, $scheme) {
* @see image_style_load()
*/
function image_style_create_derivative($style, $source, $destination) {
+ // If the source file doesn't exist, return FALSE without creating folders.
+ if (!$image = image_load($source)) {
+ return FALSE;
+ }
+
// Get the folder for the final location of this style.
$directory = drupal_dirname($destination);
@@ -876,10 +923,6 @@ function image_style_create_derivative($style, $source, $destination) {
return FALSE;
}
- if (!$image = image_load($source)) {
- return FALSE;
- }
-
foreach ($style['effects'] as $effect) {
image_effect_apply($image, $effect);
}
@@ -928,15 +971,18 @@ function image_style_transform_dimensions($style_name, array &$dimensions) {
}
/**
- * Flush cached media for a style.
+ * Flushes cached media for a style.
*
* @param $style
* An image style array.
*/
function image_style_flush($style) {
- $style_directory = drupal_realpath(file_default_scheme() . '://styles/' . $style['name']);
- if (is_dir($style_directory)) {
- file_unmanaged_delete_recursive($style_directory);
+ // Delete the style directory in each registered wrapper.
+ $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
+ foreach ($wrappers as $wrapper => $wrapper_data) {
+ if (file_exists($directory = $wrapper . '://styles/' . $style['name'])) {
+ file_unmanaged_delete_recursive($directory);
+ }
}
// Let other modules update as necessary on flush.
@@ -960,12 +1006,13 @@ function image_style_flush($style) {
}
/**
- * Return the URL for an image derivative given a style and image path.
+ * Returns the URL for an image derivative given a style and image path.
*
* @param $style_name
* The name of the style to be used with this image.
* @param $path
* The path to the image.
+ *
* @return
* The absolute URL where a style image can be downloaded, suitable for use
* in an
tag. Requesting the URL will cause the image to be created.
@@ -973,10 +1020,22 @@ function image_style_flush($style) {
*/
function image_style_url($style_name, $path) {
$uri = image_style_path($style_name, $path);
+
+ // The passed-in $path variable can be either a relative path or a full URI.
+ $original_uri = file_uri_scheme($path) ? file_stream_wrapper_uri_normalize($path) : file_build_uri($path);
+
// The token query is added even if the 'image_allow_insecure_derivatives'
// variable is TRUE, so that the emitted links remain valid if it is changed
// back to the default FALSE.
- $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, $path));
+ // However, sites which need to prevent the token query from being emitted at
+ // all can additionally set the 'image_suppress_itok_output' variable to TRUE
+ // to achieve that (if both are set, the security token will neither be
+ // emitted in the image derivative URL nor checked for in
+ // image_style_deliver()).
+ $token_query = array();
+ if (!variable_get('image_suppress_itok_output', FALSE)) {
+ $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, $original_uri));
+ }
// If not using clean URLs, the image derivative callback is only available
// with the query string. If the file does not exist, use url() to ensure
@@ -988,8 +1047,12 @@ function image_style_url($style_name, $path) {
}
$file_url = file_create_url($uri);
- // Append the query string with the token.
- return $file_url . (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query);
+ // Append the query string with the token, if necessary.
+ if ($token_query) {
+ $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query);
+ }
+
+ return $file_url;
}
/**
@@ -1014,7 +1077,7 @@ function image_style_path_token($style_name, $uri) {
}
/**
- * Return the URI of an image when using a style.
+ * Returns the URI of an image when using a style.
*
* The path returned by this function may not exist. The default generation
* method only creates images when they are requested by a user's browser.
@@ -1023,6 +1086,7 @@ function image_style_path_token($style_name, $uri) {
* The name of the style to be used with this image.
* @param $uri
* The URI or path to the image.
+ *
* @return
* The URI to an image style image.
* @see image_style_url()
@@ -1040,10 +1104,11 @@ function image_style_path($style_name, $uri) {
}
/**
- * Save a default image style to the database.
+ * Saves a default image style to the database.
*
* @param style
* An image style array provided by a module.
+ *
* @return
* An image style array. The returned style array will include the new 'isid'
* assigned to the style.
@@ -1061,7 +1126,7 @@ function image_default_style_save($style) {
}
/**
- * Revert the changes made by users to a default image style.
+ * Reverts the changes made by users to a default image style.
*
* @param style
* An image style array.
@@ -1078,7 +1143,10 @@ function image_default_style_revert($style) {
}
/**
- * Pull in image effects exposed by modules implementing hook_image_effect_info().
+ * Returns a set of image effects.
+ *
+ * These image effects are exposed by modules implementing
+ * hook_image_effect_info().
*
* @return
* An array of image effects to be used when transforming images.
@@ -1120,7 +1188,7 @@ function image_effect_definitions() {
}
/**
- * Load the definition for an image effect.
+ * Loads the definition for an image effect.
*
* The effect definition is a set of core properties for an image effect, not
* containing any user-settings. The definition defines various functions to
@@ -1132,6 +1200,7 @@ function image_effect_definitions() {
* The name of the effect definition to load.
* @param $style
* An image style array to which this effect will be added.
+ *
* @return
* An array containing the image effect definition with the following keys:
* - "effect": The unique name for the effect being performed. Usually prefixed
@@ -1159,7 +1228,7 @@ function image_effect_definition_load($effect, $style_name = NULL) {
}
/**
- * Load all image effects from the database.
+ * Loads all image effects from the database.
*
* @return
* An array of all image effects.
@@ -1191,7 +1260,7 @@ function image_effects() {
}
/**
- * Load a single image effect.
+ * Loads a single image effect.
*
* @param $ieid
* The image effect ID.
@@ -1200,6 +1269,7 @@ function image_effects() {
* @param $include
* If set, this loader will restrict to a specific type of image style, may be
* one of the defined Image style storage constants.
+ *
* @return
* An image effect array, consisting of the following keys:
* - "ieid": The unique image effect ID.
@@ -1221,10 +1291,11 @@ function image_effect_load($ieid, $style_name, $include = NULL) {
}
/**
- * Save an image effect.
+ * Saves an image effect.
*
* @param $effect
* An image effect array.
+ *
* @return
* An image effect array. In the case of a new effect, 'ieid' will be set.
*/
@@ -1241,7 +1312,7 @@ function image_effect_save($effect) {
}
/**
- * Delete an image effect.
+ * Deletes an image effect.
*
* @param $effect
* An image effect array.
@@ -1253,12 +1324,13 @@ function image_effect_delete($effect) {
}
/**
- * Given an image object and effect, perform the effect on the file.
+ * Applies an image effect to the image object.
*
* @param $image
* An image object returned by image_load().
* @param $effect
* An image effect array.
+ *
* @return
* TRUE on success. FALSE if unable to perform the image effect on the image.
*/
@@ -1309,7 +1381,7 @@ function theme_image_style($variables) {
}
/**
- * Accept a keyword (center, top, left, etc) and return it as a pixel offset.
+ * Accepts a keyword (center, top, left, etc) and returns it as a pixel offset.
*
* @param $value
* @param $current_pixels
diff --git a/modules/image/image.test b/modules/image/image.test
index d4db213..3591979 100644
--- a/modules/image/image.test
+++ b/modules/image/image.test
@@ -17,7 +17,6 @@
* image_style_save()
* image_style_delete()
* image_style_options()
- * image_style_flush()
* image_effect_definition_load()
* image_effect_load()
* image_effect_save()
@@ -122,7 +121,7 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
parent::setUp('image_module_test');
$this->style_name = 'style_foo';
- image_style_save(array('name' => $this->style_name));
+ image_style_save(array('name' => $this->style_name, 'label' => $this->randomString()));
}
/**
@@ -132,11 +131,11 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
$scheme = 'public';
$actual = image_style_path($this->style_name, "$scheme://foo/bar.gif");
$expected = "$scheme://styles/" . $this->style_name . "/$scheme/foo/bar.gif";
- $this->assertEqual($actual, $expected, t('Got the path for a file URI.'));
+ $this->assertEqual($actual, $expected, 'Got the path for a file URI.');
$actual = image_style_path($this->style_name, 'foo/bar.gif');
$expected = "$scheme://styles/" . $this->style_name . "/$scheme/foo/bar.gif";
- $this->assertEqual($actual, $expected, t('Got the path for a relative file path.'));
+ $this->assertEqual($actual, $expected, 'Got the path for a relative file path.');
}
/**
@@ -167,10 +166,27 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
$this->_testImageStyleUrlAndPath('private', FALSE);
}
+ /**
+ * Test image_style_url() with a file URL that has an extra slash in it.
+ */
+ function testImageStyleUrlExtraSlash() {
+ $this->_testImageStyleUrlAndPath('public', TRUE, TRUE);
+ }
+
+ /**
+ * Test that an invalid source image returns a 404.
+ */
+ function testImageStyleUrlForMissingSourceImage() {
+ $non_existent_uri = 'public://foo.png';
+ $generated_url = image_style_url($this->style_name, $non_existent_uri);
+ $this->drupalGet($generated_url);
+ $this->assertResponse(404, 'Accessing an image style URL with a source image that does not exist provides a 404 error response.');
+ }
+
/**
* Test image_style_url().
*/
- function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE) {
+ function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE, $extra_slash = FALSE) {
// Make the default scheme neither "public" nor "private" to verify the
// functions work for other than the default scheme.
variable_set('file_default_scheme', 'temporary');
@@ -179,7 +195,7 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
// Create the directories for the styles.
$directory = $scheme . '://styles/' . $this->style_name;
$status = file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
- $this->assertNotIdentical(FALSE, $status, t('Created the directory for the generated images for the test style.'));
+ $this->assertNotIdentical(FALSE, $status, 'Created the directory for the generated images for the test style.');
// Create a working copy of the file.
$files = $this->drupalGetTestFiles('image');
@@ -189,51 +205,76 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
// Let the image_module_test module know about this file, so it can claim
// ownership in hook_file_download().
variable_set('image_module_test_file_download', $original_uri);
- $this->assertNotIdentical(FALSE, $original_uri, t('Created the generated image file.'));
+ $this->assertNotIdentical(FALSE, $original_uri, 'Created the generated image file.');
// Get the URL of a file that has not been generated and try to create it.
$generated_uri = image_style_path($this->style_name, $original_uri);
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$generate_url = image_style_url($this->style_name, $original_uri);
+ // Ensure that the tests still pass when the file is generated by accessing
+ // a poorly constructed (but still valid) file URL that has an extra slash
+ // in it.
+ if ($extra_slash) {
+ $modified_uri = str_replace('://', ':///', $original_uri);
+ $this->assertNotEqual($original_uri, $modified_uri, 'An extra slash was added to the generated file URI.');
+ $generate_url = image_style_url($this->style_name, $modified_uri);
+ }
+
if (!$clean_url) {
$this->assertTrue(strpos($generate_url, '?q=') !== FALSE, 'When using non-clean URLS, the system path contains the query string.');
}
// Add some extra chars to the token.
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
- $this->assertResponse(403, 'Image was inaccessible at the URL wih an invalid token.');
+ $this->assertResponse(403, 'Image was inaccessible at the URL with an invalid token.');
// Change the parameter name so the token is missing.
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url));
- $this->assertResponse(403, 'Image was inaccessible at the URL wih a missing token.');
+ $this->assertResponse(403, 'Image was inaccessible at the URL with a missing token.');
+
+ // Check that the generated URL is the same when we pass in a relative path
+ // rather than a URI. We need to temporarily switch the default scheme to
+ // match the desired scheme before testing this, then switch it back to the
+ // "temporary" scheme used throughout this test afterwards.
+ variable_set('file_default_scheme', $scheme);
+ $relative_path = file_uri_target($original_uri);
+ $generate_url_from_relative_path = image_style_url($this->style_name, $relative_path);
+ $this->assertEqual($generate_url, $generate_url_from_relative_path, 'Generated URL is the same regardless of whether it came from a relative path or a file URI.');
+ variable_set('file_default_scheme', 'temporary');
// Fetch the URL that generates the file.
$this->drupalGet($generate_url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
- $this->assertRaw(file_get_contents($generated_uri), t('URL returns expected file.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
+ $this->assertRaw(file_get_contents($generated_uri), 'URL returns expected file.');
$generated_image_info = image_get_info($generated_uri);
- $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], t('Expected Content-Type was reported.'));
- $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], t('Expected Content-Length was reported.'));
+ $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], 'Expected Content-Type was reported.');
+ $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], 'Expected Content-Length was reported.');
if ($scheme == 'private') {
- $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.'));
- $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was set to prevent caching.'));
- $this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', t('Expected custom header has been added.'));
+ $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', 'Cache-Control header was set to prevent caching.');
+ $this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', 'Expected custom header has been added.');
// Make sure that a second request to the already existing derivate works
// too.
$this->drupalGet($generate_url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+
+ // Make sure that access is denied for existing style files if we do not
+ // have access.
+ variable_del('image_module_test_file_download');
+ $this->drupalGet($generate_url);
+ $this->assertResponse(403, 'Confirmed that access is denied for the private image style.');
// Repeat this with a different file that we do not have access to and
// make sure that access is denied.
$file_noaccess = array_shift($files);
$original_uri_noaccess = file_unmanaged_copy($file_noaccess->uri, $scheme . '://', FILE_EXISTS_RENAME);
$generated_uri_noaccess = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. drupal_basename($original_uri_noaccess);
- $this->assertFalse(file_exists($generated_uri_noaccess), t('Generated file does not exist.'));
+ $this->assertFalse(file_exists($generated_uri_noaccess), 'Generated file does not exist.');
$generate_url_noaccess = image_style_url($this->style_name, $original_uri_noaccess);
$this->drupalGet($generate_url_noaccess);
- $this->assertResponse(403, t('Confirmed that access is denied for the private image style.') );
+ $this->assertResponse(403, 'Confirmed that access is denied for the private image style.');
// Verify that images are not appended to the response. Currently this test only uses PNG images.
if (strpos($generate_url, '.png') === FALSE ) {
$this->fail('Confirming that private image styles are not appended require PNG file.');
@@ -247,8 +288,62 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
elseif ($clean_url) {
// Add some extra chars to the token.
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
- $this->assertResponse(200, 'Existing image was accessible at the URL wih an invalid token.');
+ $this->assertResponse(200, 'Existing image was accessible at the URL with an invalid token.');
}
+
+ // Allow insecure image derivatives to be created for the remainder of this
+ // test.
+ variable_set('image_allow_insecure_derivatives', TRUE);
+
+ // Create another working copy of the file.
+ $files = $this->drupalGetTestFiles('image');
+ $file = array_shift($files);
+ $image_info = image_get_info($file->uri);
+ $original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME);
+ // Let the image_module_test module know about this file, so it can claim
+ // ownership in hook_file_download().
+ variable_set('image_module_test_file_download', $original_uri);
+
+ // Get the URL of a file that has not been generated and try to create it.
+ $generated_uri = image_style_path($this->style_name, $original_uri);
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
+ $generate_url = image_style_url($this->style_name, $original_uri);
+
+ // Check that the image is accessible even without the security token.
+ $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url));
+ $this->assertResponse(200, 'Image was accessible at the URL with a missing token.');
+
+ // Check that a security token is still required when generating a second
+ // image derivative using the first one as a source.
+ $nested_uri = image_style_path($this->style_name, $generated_uri);
+ $nested_url = image_style_url($this->style_name, $generated_uri);
+ $nested_url_with_wrong_token = str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $nested_url);
+ $this->drupalGet($nested_url_with_wrong_token);
+ $this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token.');
+ // Check that this restriction cannot be bypassed by adding extra slashes
+ // to the URL.
+ $this->drupalGet(substr_replace($nested_url_with_wrong_token, '//styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/')));
+ $this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with an extra forward slash in the URL.');
+ $this->drupalGet(substr_replace($nested_url_with_wrong_token, '/\styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/')));
+ $this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with an extra backslash in the URL.');
+ // Make sure the image can still be generated if a correct token is used.
+ $this->drupalGet($nested_url);
+ $this->assertResponse(200, 'Image was accessible when a correct token was provided in the URL.');
+
+ // Suppress the security token in the URL, then get the URL of a file. Check
+ // that the security token is not present in the URL but that the image is
+ // still accessible.
+ variable_set('image_suppress_itok_output', TRUE);
+ $generate_url = image_style_url($this->style_name, $original_uri);
+ $this->assertIdentical(strpos($generate_url, IMAGE_DERIVATIVE_TOKEN . '='), FALSE, 'The security token does not appear in the image style URL.');
+ $this->drupalGet($generate_url);
+ $this->assertResponse(200, 'Image was accessible at the URL with a missing token.');
+
+ // Check that requesting a nonexistent image does not create any new
+ // directories in the file system.
+ $directory = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/' . $this->randomName();
+ $this->drupalGet(file_create_url($directory . '/' . $this->randomName()));
+ $this->assertFalse(file_exists($directory), 'New directory was not created in the filesystem when requesting an unauthorized image.');
}
}
@@ -274,13 +369,13 @@ class ImageEffectsUnitTest extends ImageToolkitTestCase {
* Test the image_resize_effect() function.
*/
function testResizeEffect() {
- $this->assertTrue(image_resize_effect($this->image, array('width' => 1, 'height' => 2)), t('Function returned the expected value.'));
+ $this->assertTrue(image_resize_effect($this->image, array('width' => 1, 'height' => 2)), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['resize'][0][1], 1, t('Width was passed correctly'));
- $this->assertEqual($calls['resize'][0][2], 2, t('Height was passed correctly'));
+ $this->assertEqual($calls['resize'][0][1], 1, 'Width was passed correctly');
+ $this->assertEqual($calls['resize'][0][2], 2, 'Height was passed correctly');
}
/**
@@ -288,13 +383,13 @@ class ImageEffectsUnitTest extends ImageToolkitTestCase {
*/
function testScaleEffect() {
// @todo: need to test upscaling.
- $this->assertTrue(image_scale_effect($this->image, array('width' => 10, 'height' => 10)), t('Function returned the expected value.'));
+ $this->assertTrue(image_scale_effect($this->image, array('width' => 10, 'height' => 10)), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('resize'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly'));
- $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly'));
+ $this->assertEqual($calls['resize'][0][1], 10, 'Width was passed correctly');
+ $this->assertEqual($calls['resize'][0][2], 5, 'Height was based off aspect ratio and passed correctly');
}
/**
@@ -302,42 +397,42 @@ class ImageEffectsUnitTest extends ImageToolkitTestCase {
*/
function testCropEffect() {
// @todo should test the keyword offsets.
- $this->assertTrue(image_crop_effect($this->image, array('anchor' => 'top-1', 'width' => 3, 'height' => 4)), t('Function returned the expected value.'));
+ $this->assertTrue(image_crop_effect($this->image, array('anchor' => 'top-1', 'width' => 3, 'height' => 4)), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('crop'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['crop'][0][1], 0, t('X was passed correctly'));
- $this->assertEqual($calls['crop'][0][2], 1, t('Y was passed correctly'));
- $this->assertEqual($calls['crop'][0][3], 3, t('Width was passed correctly'));
- $this->assertEqual($calls['crop'][0][4], 4, t('Height was passed correctly'));
+ $this->assertEqual($calls['crop'][0][1], 0, 'X was passed correctly');
+ $this->assertEqual($calls['crop'][0][2], 1, 'Y was passed correctly');
+ $this->assertEqual($calls['crop'][0][3], 3, 'Width was passed correctly');
+ $this->assertEqual($calls['crop'][0][4], 4, 'Height was passed correctly');
}
/**
* Test the image_scale_and_crop_effect() function.
*/
function testScaleAndCropEffect() {
- $this->assertTrue(image_scale_and_crop_effect($this->image, array('width' => 5, 'height' => 10)), t('Function returned the expected value.'));
+ $this->assertTrue(image_scale_and_crop_effect($this->image, array('width' => 5, 'height' => 10)), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('resize', 'crop'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['crop'][0][1], 7.5, t('X was computed and passed correctly'));
- $this->assertEqual($calls['crop'][0][2], 0, t('Y was computed and passed correctly'));
- $this->assertEqual($calls['crop'][0][3], 5, t('Width was computed and passed correctly'));
- $this->assertEqual($calls['crop'][0][4], 10, t('Height was computed and passed correctly'));
+ $this->assertEqual($calls['crop'][0][1], 7.5, 'X was computed and passed correctly');
+ $this->assertEqual($calls['crop'][0][2], 0, 'Y was computed and passed correctly');
+ $this->assertEqual($calls['crop'][0][3], 5, 'Width was computed and passed correctly');
+ $this->assertEqual($calls['crop'][0][4], 10, 'Height was computed and passed correctly');
}
/**
* Test the image_desaturate_effect() function.
*/
function testDesaturateEffect() {
- $this->assertTrue(image_desaturate_effect($this->image, array()), t('Function returned the expected value.'));
+ $this->assertTrue(image_desaturate_effect($this->image, array()), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('desaturate'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual(count($calls['desaturate'][0]), 1, t('Only the image was passed.'));
+ $this->assertEqual(count($calls['desaturate'][0]), 1, 'Only the image was passed.');
}
/**
@@ -345,13 +440,13 @@ class ImageEffectsUnitTest extends ImageToolkitTestCase {
*/
function testRotateEffect() {
// @todo: need to test with 'random' => TRUE
- $this->assertTrue(image_rotate_effect($this->image, array('degrees' => 90, 'bgcolor' => '#fff')), t('Function returned the expected value.'));
+ $this->assertTrue(image_rotate_effect($this->image, array('degrees' => 90, 'bgcolor' => '#fff')), 'Function returned the expected value.');
$this->assertToolkitOperationsCalled(array('rotate'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['rotate'][0][1], 90, t('Degrees were passed correctly'));
- $this->assertEqual($calls['rotate'][0][2], 0xffffff, t('Background color was passed correctly'));
+ $this->assertEqual($calls['rotate'][0][1], 90, 'Degrees were passed correctly');
+ $this->assertEqual($calls['rotate'][0][2], 0xffffff, 'Background color was passed correctly');
}
/**
@@ -417,13 +512,15 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
*/
function testNumericStyleName() {
$style_name = rand();
+ $style_label = $this->randomString();
$edit = array(
'name' => $style_name,
+ 'label' => $style_label,
);
$this->drupalPost('admin/config/media/image-styles/add', $edit, t('Create new style'));
- $this->assertRaw(t('Style %name was created.', array('%name' => $style_name)), t('Image style successfully created.'));
+ $this->assertRaw(t('Style %name was created.', array('%name' => $style_label)), 'Image style successfully created.');
$options = image_style_options();
- $this->assertTrue(array_key_exists($style_name, $options), t('Array key %key exists.', array('%key' => $style_name)));
+ $this->assertTrue(array_key_exists($style_name, $options), format_string('Array key %key exists.', array('%key' => $style_name)));
}
/**
@@ -432,6 +529,7 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
function testStyle() {
// Setup a style to be created and effects to add to it.
$style_name = strtolower($this->randomName(10));
+ $style_label = $this->randomString();
$style_path = 'admin/config/media/image-styles/edit/' . $style_name;
$effect_edits = array(
'image_resize' => array(
@@ -466,9 +564,10 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
$edit = array(
'name' => $style_name,
+ 'label' => $style_label,
);
$this->drupalPost('admin/config/media/image-styles/add', $edit, t('Create new style'));
- $this->assertRaw(t('Style %name was created.', array('%name' => $style_name)), t('Image style successfully created.'));
+ $this->assertRaw(t('Style %name was created.', array('%name' => $style_label)), 'Image style successfully created.');
// Add effect form.
@@ -490,7 +589,7 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
foreach ($style['effects'] as $ieid => $effect) {
$this->drupalGet($style_path . '/effects/' . $ieid);
foreach ($effect_edits[$effect['name']] as $field => $value) {
- $this->assertFieldByName($field, $value, t('The %field field in the %effect effect has the correct value of %value.', array('%field' => $field, '%effect' => $effect['name'], '%value' => $value)));
+ $this->assertFieldByName($field, $value, format_string('The %field field in the %effect effect has the correct value of %value.', array('%field' => $field, '%effect' => $effect['name'], '%value' => $value)));
}
}
@@ -506,14 +605,16 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
$order_correct = FALSE;
}
}
- $this->assertTrue($order_correct, t('The order of the effects is correctly set by default.'));
+ $this->assertTrue($order_correct, 'The order of the effects is correctly set by default.');
// Test the style overview form.
// Change the name of the style and adjust the weights of effects.
$style_name = strtolower($this->randomName(10));
+ $style_label = $this->randomString();
$weight = count($effect_edits);
$edit = array(
'name' => $style_name,
+ 'label' => $style_label,
);
foreach ($style['effects'] as $ieid => $effect) {
$edit['effects[' . $ieid . '][weight]'] = $weight;
@@ -522,7 +623,7 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// Create an image to make sure it gets flushed after saving.
$image_path = $this->createSampleImage($style);
- $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
+ $this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', array('%style' => $style['label'], '%file' => $image_path)));
$this->drupalPost($style_path, $edit, t('Update style'));
@@ -531,12 +632,12 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// Check that the URL was updated.
$this->drupalGet($style_path);
- $this->assertResponse(200, t('Image style %original renamed to %new', array('%original' => $style['name'], '%new' => $style_name)));
+ $this->assertResponse(200, format_string('Image style %original renamed to %new', array('%original' => $style['label'], '%new' => $style_label)));
// Check that the image was flushed after updating the style.
// This is especially important when renaming the style. Make sure that
// the old image directory has been deleted.
- $this->assertEqual($this->getImageCount($style), 0, t('Image style %style was flushed after renaming the style and updating the order of effects.', array('%style' => $style['name'])));
+ $this->assertEqual($this->getImageCount($style), 0, format_string('Image style %style was flushed after renaming the style and updating the order of effects.', array('%style' => $style['label'])));
// Load the style by the new name with the new weights.
drupal_static_reset('image_styles');
@@ -551,18 +652,18 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
$order_correct = FALSE;
}
}
- $this->assertTrue($order_correct, t('The order of the effects is correctly set by default.'));
+ $this->assertTrue($order_correct, 'The order of the effects is correctly set by default.');
// Image effect deletion form.
// Create an image to make sure it gets flushed after deleting an effect.
$image_path = $this->createSampleImage($style);
- $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
+ $this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', array('%style' => $style['label'], '%file' => $image_path)));
// Test effect deletion form.
$effect = array_pop($style['effects']);
$this->drupalPost($style_path . '/effects/' . $effect['ieid'] . '/delete', array(), t('Delete'));
- $this->assertRaw(t('The image effect %name has been deleted.', array('%name' => $effect['label'])), t('Image effect deleted.'));
+ $this->assertRaw(t('The image effect %name has been deleted.', array('%name' => $effect['label'])), 'Image effect deleted.');
// Style deletion form.
@@ -571,10 +672,10 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// Confirm the style directory has been removed.
$directory = file_default_scheme() . '://styles/' . $style_name;
- $this->assertFalse(is_dir($directory), t('Image style %style directory removed on style deletion.', array('%style' => $style['name'])));
+ $this->assertFalse(is_dir($directory), format_string('Image style %style directory removed on style deletion.', array('%style' => $style['label'])));
drupal_static_reset('image_styles');
- $this->assertFalse(image_style_load($style_name), t('Image style %style successfully deleted.', array('%style' => $style['name'])));
+ $this->assertFalse(image_style_load($style_name), format_string('Image style %style successfully deleted.', array('%style' => $style['label'])));
}
@@ -584,34 +685,36 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
function testDefaultStyle() {
// Setup a style to be created and effects to add to it.
$style_name = 'thumbnail';
+ $style_label = 'Thumbnail (100x100)';
$edit_path = 'admin/config/media/image-styles/edit/' . $style_name;
$delete_path = 'admin/config/media/image-styles/delete/' . $style_name;
$revert_path = 'admin/config/media/image-styles/revert/' . $style_name;
// Ensure deleting a default is not possible.
$this->drupalGet($delete_path);
- $this->assertText(t('Page not found'), t('Default styles may not be deleted.'));
+ $this->assertText(t('Page not found'), 'Default styles may not be deleted.');
// Ensure that editing a default is not possible (without overriding).
$this->drupalGet($edit_path);
- $this->assertNoField('edit-name', t('Default styles may not be renamed.'));
- $this->assertNoField('edit-submit', t('Default styles may not be edited.'));
- $this->assertNoField('edit-add', t('Default styles may not have new effects added.'));
+ $disabled_field = $this->xpath('//input[@id=:id and @disabled="disabled"]', array(':id' => 'edit-name'));
+ $this->assertTrue($disabled_field, 'Default styles may not be renamed.');
+ $this->assertNoField('edit-submit', 'Default styles may not be edited.');
+ $this->assertNoField('edit-add', 'Default styles may not have new effects added.');
// Create an image to make sure the default works before overriding.
drupal_static_reset('image_styles');
$style = image_style_load($style_name);
$image_path = $this->createSampleImage($style);
- $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
+ $this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
// Verify that effects attached to a default style do not have an ieid key.
foreach ($style['effects'] as $effect) {
- $this->assertFalse(isset($effect['ieid']), t('The %effect effect does not have an ieid.', array('%effect' => $effect['name'])));
+ $this->assertFalse(isset($effect['ieid']), format_string('The %effect effect does not have an ieid.', array('%effect' => $effect['name'])));
}
// Override the default.
$this->drupalPost($edit_path, array(), t('Override defaults'));
- $this->assertRaw(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $style_name)), t('Default image style may be overridden.'));
+ $this->assertRaw(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $style_label)), 'Default image style may be overridden.');
// Add sample effect to the overridden style.
$this->drupalPost($edit_path, array('new' => 'image_desaturate'), t('Add'));
@@ -620,22 +723,23 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// Verify that effects attached to the style have an ieid now.
foreach ($style['effects'] as $effect) {
- $this->assertTrue(isset($effect['ieid']), t('The %effect effect has an ieid.', array('%effect' => $effect['name'])));
+ $this->assertTrue(isset($effect['ieid']), format_string('The %effect effect has an ieid.', array('%effect' => $effect['name'])));
}
// The style should now have 2 effect, the original scale provided by core
// and the desaturate effect we added in the override.
$effects = array_values($style['effects']);
- $this->assertEqual($effects[0]['name'], 'image_scale', t('The default effect still exists in the overridden style.'));
- $this->assertEqual($effects[1]['name'], 'image_desaturate', t('The added effect exists in the overridden style.'));
+ $this->assertEqual($effects[0]['name'], 'image_scale', 'The default effect still exists in the overridden style.');
+ $this->assertEqual($effects[1]['name'], 'image_desaturate', 'The added effect exists in the overridden style.');
- // Check that we are unable to rename an overridden style.
+ // Check that we are able to rename an overridden style.
$this->drupalGet($edit_path);
- $this->assertNoField('edit-name', t('Overridden styles may not be renamed.'));
+ $disabled_field = $this->xpath('//input[@id=:id and @disabled="disabled"]', array(':id' => 'edit-name'));
+ $this->assertFalse($disabled_field, 'Overridden styles may be renamed.');
// Create an image to ensure the override works properly.
$image_path = $this->createSampleImage($style);
- $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
+ $this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', array('%style' => $style['label'], '%file' => $image_path)));
// Revert the image style.
$this->drupalPost($revert_path, array(), t('Revert'));
@@ -644,8 +748,8 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// The style should now have the single effect for scale.
$effects = array_values($style['effects']);
- $this->assertEqual($effects[0]['name'], 'image_scale', t('The default effect still exists in the reverted style.'));
- $this->assertFalse(array_key_exists(1, $effects), t('The added effect has been removed in the reverted style.'));
+ $this->assertEqual($effects[0]['name'], 'image_scale', 'The default effect still exists in the reverted style.');
+ $this->assertFalse(array_key_exists(1, $effects), 'The added effect has been removed in the reverted style.');
}
/**
@@ -654,7 +758,8 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
function testStyleReplacement() {
// Create a new style.
$style_name = strtolower($this->randomName(10));
- image_style_save(array('name' => $style_name));
+ $style_label = $this->randomString();
+ image_style_save(array('name' => $style_name, 'label' => $style_label));
$style_path = 'admin/config/media/image-styles/edit/' . $style_name;
// Create an image field that uses the new style.
@@ -672,28 +777,30 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// Test that image is displayed using newly created style.
$this->drupalGet('node/' . $nid);
- $this->assertRaw(check_plain(image_style_url($style_name, $node->{$field_name}[LANGUAGE_NONE][0]['uri'])), t('Image displayed using style @style.', array('@style' => $style_name)));
+ $this->assertRaw(check_plain(image_style_url($style_name, $node->{$field_name}[LANGUAGE_NONE][0]['uri'])), format_string('Image displayed using style @style.', array('@style' => $style_name)));
// Rename the style and make sure the image field is updated.
$new_style_name = strtolower($this->randomName(10));
+ $new_style_label = $this->randomString();
$edit = array(
'name' => $new_style_name,
+ 'label' => $new_style_label,
);
$this->drupalPost('admin/config/media/image-styles/edit/' . $style_name, $edit, t('Update style'));
- $this->assertText(t('Changes to the style have been saved.'), t('Style %name was renamed to %new_name.', array('%name' => $style_name, '%new_name' => $new_style_name)));
+ $this->assertText(t('Changes to the style have been saved.'), format_string('Style %name was renamed to %new_name.', array('%name' => $style_name, '%new_name' => $new_style_name)));
$this->drupalGet('node/' . $nid);
- $this->assertRaw(check_plain(image_style_url($new_style_name, $node->{$field_name}[LANGUAGE_NONE][0]['uri'])), t('Image displayed using style replacement style.'));
+ $this->assertRaw(check_plain(image_style_url($new_style_name, $node->{$field_name}[LANGUAGE_NONE][0]['uri'])), format_string('Image displayed using style replacement style.'));
// Delete the style and choose a replacement style.
$edit = array(
'replacement' => 'thumbnail',
);
$this->drupalPost('admin/config/media/image-styles/delete/' . $new_style_name, $edit, t('Delete'));
- $message = t('Style %name was deleted.', array('%name' => $new_style_name));
+ $message = t('Style %name was deleted.', array('%name' => $new_style_label));
$this->assertRaw($message, $message);
$this->drupalGet('node/' . $nid);
- $this->assertRaw(check_plain(image_style_url('thumbnail', $node->{$field_name}[LANGUAGE_NONE][0]['uri'])), t('Image displayed using style replacement style.'));
+ $this->assertRaw(check_plain(image_style_url('thumbnail', $node->{$field_name}[LANGUAGE_NONE][0]['uri'])), format_string('Image displayed using style replacement style.'));
}
}
@@ -744,7 +851,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
'height' => 20,
);
$default_output = theme('image', $image_info);
- $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.'));
+ $this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
// Test the image linked to file formatter.
$instance = field_info_instance('node', $field_name, 'article');
@@ -753,20 +860,19 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
field_update_instance($instance);
$default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE));
$this->drupalGet('node/' . $nid);
- $this->assertRaw($default_output, t('Image linked to file formatter displaying correctly on full node view.'));
+ $this->assertRaw($default_output, 'Image linked to file formatter displaying correctly on full node view.');
// Verify that the image can be downloaded.
- $this->assertEqual(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), t('File was downloaded successfully.'));
+ $this->assertEqual(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), 'File was downloaded successfully.');
if ($scheme == 'private') {
// Only verify HTTP headers when using private scheme and the headers are
// sent by Drupal.
- $this->assertEqual($this->drupalGetHeader('Content-Type'), 'image/png', t('Content-Type header was sent.'));
- $this->assertEqual($this->drupalGetHeader('Content-Disposition'), 'inline; filename="' . $test_image->filename . '"', t('Content-Disposition header was sent.'));
- $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'private', t('Cache-Control header was sent.'));
+ $this->assertEqual($this->drupalGetHeader('Content-Type'), 'image/png', 'Content-Type header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'private', 'Cache-Control header was sent.');
// Log out and try to access the file.
$this->drupalLogout();
$this->drupalGet(file_create_url($image_uri));
- $this->assertResponse('403', t('Access denied to original image as anonymous user.'));
+ $this->assertResponse('403', 'Access denied to original image as anonymous user.');
// Log in again.
$this->drupalLogin($this->admin_user);
@@ -777,7 +883,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
field_update_instance($instance);
$default_output = l(theme('image', $image_info), 'node/' . $nid, array('html' => TRUE, 'attributes' => array('class' => 'active')));
$this->drupalGet('node/' . $nid);
- $this->assertRaw($default_output, t('Image linked to content formatter displaying correctly on full node view.'));
+ $this->assertRaw($default_output, 'Image linked to content formatter displaying correctly on full node view.');
// Test the image style 'thumbnail' formatter.
$instance['display']['default']['settings']['image_link'] = '';
@@ -793,13 +899,13 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
$image_info['height'] = 50;
$default_output = theme('image', $image_info);
$this->drupalGet('node/' . $nid);
- $this->assertRaw($default_output, t('Image style thumbnail formatter displaying correctly on full node view.'));
+ $this->assertRaw($default_output, 'Image style thumbnail formatter displaying correctly on full node view.');
if ($scheme == 'private') {
// Log out and try to access the file.
$this->drupalLogout();
$this->drupalGet(image_style_url('thumbnail', $image_uri));
- $this->assertResponse('403', t('Access denied to image style thumbnail as anonymous user.'));
+ $this->assertResponse('403', 'Access denied to image style thumbnail as anonymous user.');
}
}
@@ -828,16 +934,16 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
$instance = field_info_instance('node', $field_name, 'article');
$this->drupalGet('node/add/article');
- $this->assertText(t('Files must be less than 50 KB.'), t('Image widget max file size is displayed on article form.'));
- $this->assertText(t('Allowed file types: ' . $test_image_extension . '.'), t('Image widget allowed file types displayed on article form.'));
- $this->assertText(t('Images must be between 10x10 and 100x100 pixels.'), t('Image widget allowed resolution displayed on article form.'));
+ $this->assertText(t('Files must be less than 50 KB.'), 'Image widget max file size is displayed on article form.');
+ $this->assertText(t('Allowed file types: ' . $test_image_extension . '.'), 'Image widget allowed file types displayed on article form.');
+ $this->assertText(t('Images must be between 10x10 and 100x100 pixels.'), 'Image widget allowed resolution displayed on article form.');
// We have to create the article first and then edit it because the alt
// and title fields do not display until the image has been attached.
$nid = $this->uploadNodeImage($test_image, $field_name, 'article');
$this->drupalGet('node/' . $nid . '/edit');
- $this->assertFieldByName($field_name . '[' . LANGUAGE_NONE . '][0][alt]', '', t('Alt field displayed on article form.'));
- $this->assertFieldByName($field_name . '[' . LANGUAGE_NONE . '][0][title]', '', t('Title field displayed on article form.'));
+ $this->assertFieldByName($field_name . '[' . LANGUAGE_NONE . '][0][alt]', '', 'Alt field displayed on article form.');
+ $this->assertFieldByName($field_name . '[' . LANGUAGE_NONE . '][0][title]', '', 'Title field displayed on article form.');
// Verify that the attached image is being previewed using the 'medium'
// style.
$node = node_load($nid, NULL, TRUE);
@@ -847,7 +953,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
'height' => 110,
);
$default_output = theme('image', $image_info);
- $this->assertRaw($default_output, t("Preview image is displayed using 'medium' style."));
+ $this->assertRaw($default_output, "Preview image is displayed using 'medium' style.");
// Add alt/title fields to the image and verify that they are displayed.
$image_info = array(
@@ -863,7 +969,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
);
$this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
$default_output = theme('image', $image_info);
- $this->assertRaw($default_output, t('Image displayed using user supplied alt and title attributes.'));
+ $this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.');
// Verify that alt/title longer than allowed results in a validation error.
$test_size = 2000;
@@ -896,7 +1002,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
'title' => t('Image field formatter'),
),
));
- $this->assertTrue(stripos($image, 'testFound') > 0, t('Image field formatters can have attributes.'));
+ $this->assertTrue(stripos($image, 'testFound') > 0, 'Image field formatters can have attributes.');
}
/**
@@ -913,7 +1019,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
$this->drupalGet('node/' . $node->nid);
// Verify that no image is displayed on the page by checking for the class
// that would be used on the image field.
- $this->assertNoPattern('', t('No image displayed when no image is attached and no default image specified.'));
+ $this->assertNoPattern('', 'No image displayed when no image is attached and no default image specified.');
// Add a default image to the public imagefield instance.
$images = $this->drupalGetTestFiles('image');
@@ -925,10 +1031,10 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
field_info_cache_clear();
$field = field_info_field($field_name);
$image = file_load($field['settings']['default_image']);
- $this->assertTrue($image->status == FILE_STATUS_PERMANENT, t('The default image status is permanent.'));
+ $this->assertTrue($image->status == FILE_STATUS_PERMANENT, 'The default image status is permanent.');
$default_output = theme('image', array('path' => $image->uri));
$this->drupalGet('node/' . $node->nid);
- $this->assertRaw($default_output, t('Default image displayed when no user supplied image is present.'));
+ $this->assertRaw($default_output, 'Default image displayed when no user supplied image is present.');
// Create a node with an image attached and ensure that the default image
// is not displayed.
@@ -941,8 +1047,8 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
);
$image_output = theme('image', $image_info);
$this->drupalGet('node/' . $nid);
- $this->assertNoRaw($default_output, t('Default image is not displayed when user supplied image is present.'));
- $this->assertRaw($image_output, t('User supplied image is displayed.'));
+ $this->assertNoRaw($default_output, 'Default image is not displayed when user supplied image is present.');
+ $this->assertRaw($image_output, 'User supplied image is displayed.');
// Remove default image from the field and make sure it is no longer used.
$edit = array(
@@ -952,7 +1058,7 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
// Clear field info cache so the new default image is detected.
field_info_cache_clear();
$field = field_info_field($field_name);
- $this->assertFalse($field['settings']['default_image'], t('Default image removed from field.'));
+ $this->assertFalse($field['settings']['default_image'], 'Default image removed from field.');
// Create an image field that uses the private:// scheme and test that the
// default image works as expected.
$private_field_name = strtolower($this->randomName());
@@ -964,14 +1070,14 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase {
$this->drupalPost('admin/structure/types/manage/article/fields/' . $private_field_name, $edit, t('Save settings'));
$private_field = field_info_field($private_field_name);
$image = file_load($private_field['settings']['default_image']);
- $this->assertEqual('private', file_uri_scheme($image->uri), t('Default image uses private:// scheme.'));
- $this->assertTrue($image->status == FILE_STATUS_PERMANENT, t('The default image status is permanent.'));
+ $this->assertEqual('private', file_uri_scheme($image->uri), 'Default image uses private:// scheme.');
+ $this->assertTrue($image->status == FILE_STATUS_PERMANENT, 'The default image status is permanent.');
// Create a new node with no image attached and ensure that default private
// image is displayed.
$node = $this->drupalCreateNode(array('type' => 'article'));
$default_output = theme('image', array('path' => $image->uri));
$this->drupalGet('node/' . $node->nid);
- $this->assertRaw($default_output, t('Default private image displayed when no user supplied image is present.'));
+ $this->assertRaw($default_output, 'Default private image displayed when no user supplied image is present.');
}
}
@@ -1017,9 +1123,9 @@ class ImageFieldValidateTestCase extends ImageFieldTestCase {
}
}
$nid = $this->uploadNodeImage($image_that_is_too_small, $field_name, 'article');
- $this->assertText(t('The specified file ' . $image_that_is_too_small->filename . ' could not be uploaded. The image is too small; the minimum dimensions are 50x50 pixels.'), t('Node save failed when minimum image resolution was not met.'));
+ $this->assertText(t('The specified file ' . $image_that_is_too_small->filename . ' could not be uploaded. The image is too small; the minimum dimensions are 50x50 pixels.'), 'Node save failed when minimum image resolution was not met.');
$nid = $this->uploadNodeImage($image_that_is_too_big, $field_name, 'article');
- $this->assertText(t('The image was resized to fit within the maximum allowed dimensions of 100x100 pixels.'), t('Image exceeding max resolution was properly resized.'));
+ $this->assertText(t('The image was resized to fit within the maximum allowed dimensions of 100x100 pixels.'), 'Image exceeding max resolution was properly resized.');
}
}
@@ -1050,7 +1156,7 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
// Create a style.
- $style = image_style_save(array('name' => 'test'));
+ $style = image_style_save(array('name' => 'test', 'label' => 'Test'));
$generated_uri = 'public://styles/test/public/'. drupal_basename($original_uri);
$url = image_style_url('test', $original_uri);
@@ -1074,14 +1180,14 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 120, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 60, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 120, 'Expected width was found.');
+ $this->assertEqual($image_info['height'], 60, 'Expected height was found.');
// Rotate 90 degrees anticlockwise.
$effect = array(
@@ -1095,14 +1201,14 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 60, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 120, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 60, 'Expected width was found.');
+ $this->assertEqual($image_info['height'], 120, 'Expected height was found.');
// Scale an image that is higher than it is wide (rotated by previous effect).
$effect = array(
@@ -1117,14 +1223,14 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 45, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 90, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 45, 'Expected width was found.');
+ $this->assertEqual($image_info['height'], 90, 'Expected height was found.');
// Test upscale disabled.
$effect = array(
@@ -1139,14 +1245,14 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 45, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 90, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 45, 'Expected width was found.');
+ $this->assertEqual($image_info['height'], 90, 'Expected height was found.');
// Add a desaturate effect.
$effect = array(
@@ -1157,14 +1263,14 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 45, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 90, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 45, 'Expected width was found.');
+ $this->assertEqual($image_info['height'], 90, 'Expected height was found.');
// Add a random rotate effect.
$effect = array(
@@ -1178,11 +1284,11 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
// Add a crop effect.
@@ -1198,14 +1304,14 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 30, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 30, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 30, 'Expected width was found.');
+ $this->assertEqual($image_info['height'], 30, 'Expected height was found.');
// Rotate to a non-multiple of 90 degrees.
$effect = array(
@@ -1219,11 +1325,11 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
$effect = image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
- $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
+ $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
$this->drupalGet($url);
- $this->assertResponse(200, t('Image was generated at the URL.'));
- $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
+ $this->assertResponse(200, 'Image was generated at the URL.');
+ $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
image_effect_delete($effect);
@@ -1237,7 +1343,7 @@ class ImageDimensionsTestCase extends DrupalWebTestCase {
image_effect_save($effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '
', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '
', 'Expected img tag was found.');
}
}
@@ -1371,13 +1477,13 @@ class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
$return_value = image_dimensions_scale($test['input']['dimensions'], $test['input']['width'], $test['input']['height'], $test['input']['upscale']);
// Check the width.
- $this->assertEqual($test['output']['dimensions']['width'], $test['input']['dimensions']['width'], t('Computed width (@computed_width) equals expected width (@expected_width)', array('@computed_width' => $test['output']['dimensions']['width'], '@expected_width' => $test['input']['dimensions']['width'])));
+ $this->assertEqual($test['output']['dimensions']['width'], $test['input']['dimensions']['width'], format_string('Computed width (@computed_width) equals expected width (@expected_width)', array('@computed_width' => $test['output']['dimensions']['width'], '@expected_width' => $test['input']['dimensions']['width'])));
// Check the height.
- $this->assertEqual($test['output']['dimensions']['height'], $test['input']['dimensions']['height'], t('Computed height (@computed_height) equals expected height (@expected_height)', array('@computed_height' => $test['output']['dimensions']['height'], '@expected_height' => $test['input']['dimensions']['height'])));
+ $this->assertEqual($test['output']['dimensions']['height'], $test['input']['dimensions']['height'], format_string('Computed height (@computed_height) equals expected height (@expected_height)', array('@computed_height' => $test['output']['dimensions']['height'], '@expected_height' => $test['input']['dimensions']['height'])));
// Check the return value.
- $this->assertEqual($test['output']['return_value'], $return_value, t('Correct return value.'));
+ $this->assertEqual($test['output']['return_value'], $return_value, 'Correct return value.');
}
}
}
@@ -1638,7 +1744,7 @@ class ImageThemeFunctionWebTestCase extends DrupalWebTestCase {
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
// Create a style.
- image_style_save(array('name' => 'test'));
+ image_style_save(array('name' => 'test', 'label' => 'Test'));
$url = image_style_url('test', $original_uri);
// Test using theme_image_formatter() without an image title, alt text, or
@@ -1671,3 +1777,108 @@ class ImageThemeFunctionWebTestCase extends DrupalWebTestCase {
}
}
+
+/**
+ * Tests flushing of image styles.
+ */
+class ImageStyleFlushTest extends ImageFieldTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Image style flushing',
+ 'description' => 'Tests flushing of image styles.',
+ 'group' => 'Image',
+ );
+ }
+
+ /**
+ * Given an image style and a wrapper, generate an image.
+ */
+ function createSampleImage($style, $wrapper) {
+ static $file;
+
+ if (!isset($file)) {
+ $files = $this->drupalGetTestFiles('image');
+ $file = reset($files);
+ }
+
+ // Make sure we have an image in our wrapper testing file directory.
+ $source_uri = file_unmanaged_copy($file->uri, $wrapper . '://');
+ // Build the derivative image.
+ $derivative_uri = image_style_path($style['name'], $source_uri);
+ $derivative = image_style_create_derivative($style, $source_uri, $derivative_uri);
+
+ return $derivative ? $derivative_uri : FALSE;
+ }
+
+ /**
+ * Count the number of images currently created for a style in a wrapper.
+ */
+ function getImageCount($style, $wrapper) {
+ return count(file_scan_directory($wrapper . '://styles/' . $style['name'], '/.*/'));
+ }
+
+ /**
+ * General test to flush a style.
+ */
+ function testFlush() {
+
+ // Setup a style to be created and effects to add to it.
+ $style_name = strtolower($this->randomName(10));
+ $style_label = $this->randomString();
+ $style_path = 'admin/config/media/image-styles/edit/' . $style_name;
+ $effect_edits = array(
+ 'image_resize' => array(
+ 'data[width]' => 100,
+ 'data[height]' => 101,
+ ),
+ 'image_scale' => array(
+ 'data[width]' => 110,
+ 'data[height]' => 111,
+ 'data[upscale]' => 1,
+ ),
+ );
+
+ // Add style form.
+ $edit = array(
+ 'name' => $style_name,
+ 'label' => $style_label,
+ );
+ $this->drupalPost('admin/config/media/image-styles/add', $edit, t('Create new style'));
+ // Add each sample effect to the style.
+ foreach ($effect_edits as $effect => $edit) {
+ // Add the effect.
+ $this->drupalPost($style_path, array('new' => $effect), t('Add'));
+ if (!empty($edit)) {
+ $this->drupalPost(NULL, $edit, t('Add effect'));
+ }
+ }
+
+ // Load the saved image style.
+ $style = image_style_load($style_name);
+
+ // Create an image for the 'public' wrapper.
+ $image_path = $this->createSampleImage($style, 'public');
+ // Expecting to find 2 images, one is the sample.png image shown in
+ // image style preview.
+ $this->assertEqual($this->getImageCount($style, 'public'), 2, format_string('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
+
+ // Create an image for the 'private' wrapper.
+ $image_path = $this->createSampleImage($style, 'private');
+ $this->assertEqual($this->getImageCount($style, 'private'), 1, format_string('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
+
+ // Remove the 'image_scale' effect and updates the style, which in turn
+ // forces an image style flush.
+ $effect = array_pop($style['effects']);
+ $this->drupalPost($style_path . '/effects/' . $effect['ieid'] . '/delete', array(), t('Delete'));
+ $this->assertResponse(200);
+ $this->drupalPost($style_path, array(), t('Update style'));
+ $this->assertResponse(200);
+
+ // Post flush, expected 1 image in the 'public' wrapper (sample.png).
+ $this->assertEqual($this->getImageCount($style, 'public'), 1, format_string('Image style %style flushed correctly for %wrapper wrapper.', array('%style' => $style['name'], '%wrapper' => 'public')));
+
+ // Post flush, expected no image in the 'private' wrapper.
+ $this->assertEqual($this->getImageCount($style, 'private'), 0, format_string('Image style %style flushed correctly for %wrapper wrapper.', array('%style' => $style['name'], '%wrapper' => 'private')));
+ }
+}
diff --git a/modules/image/tests/image_module_test.info b/modules/image/tests/image_module_test.info
index db7eacd..d382a73 100644
--- a/modules/image/tests/image_module_test.info
+++ b/modules/image/tests/image_module_test.info
@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
files[] = image_module_test.module
hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc
index 2535357..e813962 100644
--- a/modules/locale/locale.admin.inc
+++ b/modules/locale/locale.admin.inc
@@ -388,13 +388,13 @@ function locale_languages_edit_form_validate($form, &$form_state) {
form_set_error('prefix', t('Domain and path prefix values should not be set at the same time.'));
}
if (!empty($form_state['values']['domain']) && $duplicate = db_query("SELECT language FROM {languages} WHERE domain = :domain AND language <> :language", array(':domain' => $form_state['values']['domain'], ':language' => $form_state['values']['langcode']))->fetchField()) {
- form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language)));
+ form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate)));
}
if (empty($form_state['values']['prefix']) && language_default('language') != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
form_set_error('prefix', t('Only the default language can have both the domain and prefix empty.'));
}
if (!empty($form_state['values']['prefix']) && $duplicate = db_query("SELECT language FROM {languages} WHERE prefix = :prefix AND language <> :language", array(':prefix' => $form_state['values']['prefix'], ':language' => $form_state['values']['langcode']))->fetchField()) {
- form_set_error('prefix', t('The prefix (%prefix) is already tied to a language (%language).', array('%prefix' => $form_state['values']['prefix'], '%language' => $duplicate->language)));
+ form_set_error('prefix', t('The prefix (%prefix) is already tied to a language (%language).', array('%prefix' => $form_state['values']['prefix'], '%language' => $duplicate)));
}
}
@@ -1139,11 +1139,11 @@ function locale_translate_edit_form($form, &$form_state, $lid) {
'#value' => $source->location
);
- // Include default form controls with empty values for all languages.
- // This ensures that the languages are always in the same order in forms.
+ // Include both translated and not yet translated target languages in the
+ // list. The source language is English for built-in strings and the default
+ // language for other strings.
$languages = language_list();
$default = language_default();
- // We don't need the default language value, that value is in $source.
$omit = $source->textgroup == 'default' ? 'en' : $default->language;
unset($languages[($omit)]);
$form['translations'] = array('#tree' => TRUE);
@@ -1242,9 +1242,7 @@ function locale_translate_delete_page($lid) {
if ($source = db_query('SELECT lid, source FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject()) {
return drupal_get_form('locale_translate_delete_form', $source);
}
- else {
- return drupal_not_found();
- }
+ return MENU_NOT_FOUND;
}
/**
diff --git a/modules/locale/locale.info b/modules/locale/locale.info
index 1d6bdcf..7431c7b 100644
--- a/modules/locale/locale.info
+++ b/modules/locale/locale.info
@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
files[] = locale.test
configure = admin/config/regional/language
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/locale/locale.module b/modules/locale/locale.module
index 94e7cd1..768fead 100644
--- a/modules/locale/locale.module
+++ b/modules/locale/locale.module
@@ -386,20 +386,53 @@ function locale_form_node_form_alter(&$form, &$form_state) {
/**
* Form submit handler for node_form().
*
- * Checks if Locale is registered as a translation handler and handle possible
- * node language changes.
- *
* This submit handler needs to run before entity_form_submit_build_entity()
* is invoked by node_form_submit_build_node(), because it alters the values of
* attached fields. Therefore, it cannot be a hook_node_submit() implementation.
*/
function locale_field_node_form_submit($form, &$form_state) {
- if (field_has_translation_handler('node', 'locale')) {
- $node = (object) $form_state['values'];
- $current_language = entity_language('node', $node);
- list(, , $bundle) = entity_extract_ids('node', $node);
+ locale_field_entity_form_submit('node', $form, $form_state);
+}
- foreach (field_info_instances('node', $bundle) as $instance) {
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
+ // If a content type has multilingual support we set the content language as
+ // comment language.
+ if ($form['language']['#value'] == LANGUAGE_NONE && locale_multilingual_node_type($form['#node']->type)) {
+ global $language_content;
+ $form['language']['#value'] = $language_content->language;
+ $submit_callback = 'locale_field_comment_form_submit';
+ array_unshift($form['actions']['preview']['#submit'], $submit_callback);
+ array_unshift($form['#submit'], $submit_callback);
+ }
+}
+
+/**
+ * Form submit handler for comment_form().
+ *
+ * This submit handler needs to run before entity_form_submit_build_entity()
+ * is invoked by comment_form_submit_build_comment(), because it alters the
+ * values of attached fields.
+ */
+function locale_field_comment_form_submit($form, &$form_state) {
+ locale_field_entity_form_submit('comment', $form, $form_state);
+}
+
+/**
+ * Handles field language on submit for the given entity type.
+ *
+ * Checks if Locale is registered as a translation handler and handle possible
+ * language changes.
+ */
+function locale_field_entity_form_submit($entity_type, $form, &$form_state ) {
+ if (field_has_translation_handler($entity_type, 'locale')) {
+ $entity = (object) $form_state['values'];
+ $current_language = entity_language($entity_type, $entity);
+ list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
$previous_language = $form[$field_name]['#language'];
@@ -407,7 +440,7 @@ 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 != $current_language) {
- $form_state['values'][$field_name][$current_language] = $node->{$field_name}[$previous_language];
+ $form_state['values'][$field_name][$current_language] = $entity->{$field_name}[$previous_language];
$form_state['values'][$field_name][$previous_language] = array();
}
}
@@ -491,6 +524,9 @@ function locale_field_language_fallback(&$display_language, $entity, $langcode)
*/
function locale_entity_info_alter(&$entity_info) {
$entity_info['node']['translation']['locale'] = TRUE;
+ if (isset($entity_info['comment'])) {
+ $entity_info['comment']['translation']['locale'] = TRUE;
+ }
}
/**
@@ -936,7 +972,7 @@ function locale_css_alter(&$css) {
// Replicate the same item, but with the RTL path and a little larger
// weight so that it appears directly after the original CSS file.
$item['data'] = $rtl_path;
- $item['weight'] += 0.01;
+ $item['weight'] += 0.0001;
$css[$rtl_path] = $item;
}
}
@@ -1060,15 +1096,3 @@ function locale_url_outbound_alter(&$path, &$options, $original_path) {
}
}
}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function locale_form_comment_form_alter(&$form, &$form_state, $form_id) {
- // If a content type has multilingual support we set the content language as
- // comment language.
- if ($form['language']['#value'] == LANGUAGE_NONE && locale_multilingual_node_type($form['#node']->type)) {
- global $language_content;
- $form['language']['#value'] = $language_content->language;
- }
-}
diff --git a/modules/locale/locale.test b/modules/locale/locale.test
index 632506e..9086587 100644
--- a/modules/locale/locale.test
+++ b/modules/locale/locale.test
@@ -52,8 +52,8 @@ class LocaleConfigurationTest extends DrupalWebTestCase {
'langcode' => 'fr',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
- $this->assertText('fr', t('Language added successfully.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertText('fr', 'Language added successfully.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
// Add custom language.
// Code for the language.
@@ -72,109 +72,109 @@ class LocaleConfigurationTest extends DrupalWebTestCase {
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertText($langcode, t('Language code found.'));
- $this->assertText($name, t('Name found.'));
- $this->assertText($native, t('Native found.'));
- $this->assertText($native, t('Test language added.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertText($langcode, 'Language code found.');
+ $this->assertText($name, 'Name found.');
+ $this->assertText($native, 'Native found.');
+ $this->assertText($native, 'Test language added.');
// Check if we can change the default language.
$path = 'admin/config/regional/language';
$this->drupalGet($path);
- $this->assertFieldChecked('edit-site-default-en', t('English is the default language.'));
+ $this->assertFieldChecked('edit-site-default-en', 'English is the default language.');
// Change the default language.
$edit = array(
'site_default' => $langcode,
);
$this->drupalPost(NULL, $edit, t('Save configuration'));
- $this->assertNoFieldChecked('edit-site-default-en', t('Default language updated.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertNoFieldChecked('edit-site-default-en', 'Default language updated.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
// Check if a valid language prefix is added after changing the default
// language.
$this->drupalGet('admin/config/regional/language/edit/en');
- $this->assertFieldByXPath('//input[@name="prefix"]', 'en', t('A valid path prefix has been added to the previous default language.'));
+ $this->assertFieldByXPath('//input[@name="prefix"]', 'en', 'A valid path prefix has been added to the previous default language.');
// Ensure we can't delete the default language.
$this->drupalGet('admin/config/regional/language/delete/' . $langcode);
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertText(t('The default language cannot be deleted.'), t('Failed to delete the default language.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertText(t('The default language cannot be deleted.'), 'Failed to delete the default language.');
// Check if we can disable a language.
$edit = array(
'enabled[en]' => FALSE,
);
$this->drupalPost($path, $edit, t('Save configuration'));
- $this->assertNoFieldChecked('edit-enabled-en', t('Language disabled.'));
+ $this->assertNoFieldChecked('edit-enabled-en', 'Language disabled.');
// Set disabled language to be the default and ensure it is re-enabled.
$edit = array(
'site_default' => 'en',
);
$this->drupalPost(NULL, $edit, t('Save configuration'));
- $this->assertFieldChecked('edit-enabled-en', t('Default language re-enabled.'));
+ $this->assertFieldChecked('edit-enabled-en', 'Default language re-enabled.');
// Ensure 'edit' link works.
$this->clickLink(t('edit'));
- $this->assertTitle(t('Edit language | Drupal'), t('Page title is "Edit language".'));
+ $this->assertTitle(t('Edit language | Drupal'), 'Page title is "Edit language".');
// Edit a language.
$name = $this->randomName(16);
$edit = array(
'name' => $name,
);
$this->drupalPost('admin/config/regional/language/edit/' . $langcode, $edit, t('Save language'));
- $this->assertRaw($name, t('The language has been updated.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertRaw($name, 'The language has been updated.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
// Ensure 'delete' link works.
$this->drupalGet('admin/config/regional/language');
$this->clickLink(t('delete'));
- $this->assertText(t('Are you sure you want to delete the language'), t('"delete" link is correct.'));
+ $this->assertText(t('Are you sure you want to delete the language'), '"delete" link is correct.');
// Delete an enabled language.
$this->drupalGet('admin/config/regional/language/delete/' . $langcode);
// First test the 'cancel' link.
$this->clickLink(t('Cancel'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertRaw($name, t('The language was not deleted.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertRaw($name, 'The language was not deleted.');
// Delete the language for real. This a confirm form, we do not need any
// fields changed.
$this->drupalPost('admin/config/regional/language/delete/' . $langcode, array(), t('Delete'));
// We need raw here because %locale will add HTML.
- $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), t('The test language has been removed.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), 'The test language has been removed.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
// Verify that language is no longer found.
$this->drupalGet('admin/config/regional/language/delete/' . $langcode);
- $this->assertResponse(404, t('Language no longer found.'));
+ $this->assertResponse(404, 'Language no longer found.');
// Make sure the "language_count" variable has been updated correctly.
drupal_static_reset('language_list');
$enabled = language_list('enabled');
- $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), t('Language count is correct.'));
+ $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), 'Language count is correct.');
// Delete a disabled language.
// Disable an enabled language.
$edit = array(
'enabled[fr]' => FALSE,
);
$this->drupalPost($path, $edit, t('Save configuration'));
- $this->assertNoFieldChecked('edit-enabled-fr', t('French language disabled.'));
+ $this->assertNoFieldChecked('edit-enabled-fr', 'French language disabled.');
// Get the count of enabled languages.
drupal_static_reset('language_list');
$enabled = language_list('enabled');
// Delete the disabled language.
$this->drupalPost('admin/config/regional/language/delete/fr', array(), t('Delete'));
// We need raw here because %locale will add HTML.
- $this->assertRaw(t('The language %locale has been removed.', array('%locale' => 'French')), t('Disabled language has been removed.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertRaw(t('The language %locale has been removed.', array('%locale' => 'French')), 'Disabled language has been removed.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
// Verify that language is no longer found.
$this->drupalGet('admin/config/regional/language/delete/fr');
- $this->assertResponse(404, t('Language no longer found.'));
+ $this->assertResponse(404, 'Language no longer found.');
// Make sure the "language_count" variable has not changed.
- $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), t('Language count is correct.'));
+ $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), 'Language count is correct.');
// Ensure we can't delete the English language.
$this->drupalGet('admin/config/regional/language/delete/en');
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertText(t('The English language cannot be deleted.'), t('Failed to delete English language.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertText(t('The English language cannot be deleted.'), 'Failed to delete English language.');
}
}
@@ -205,7 +205,7 @@ class LocaleLibraryInfoAlterTest extends DrupalWebTestCase {
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.'));
+ $this->assertTrue(strpos($scripts, 'locale.datepicker.js'), 'locale.datepicker.js added to scripts.');
}
}
@@ -289,13 +289,13 @@ class LocaleJavascriptTranslationTest extends DrupalWebTestCase {
$args = array('%source' => $str, '%context' => $context);
// Make sure that the string was found in the file.
- $this->assertTrue(isset($source_strings[$str]), t("Found source string: %source", $args));
+ $this->assertTrue(isset($source_strings[$str]), format_string('Found source string: %source', $args));
// Make sure that the proper context was matched.
- $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? t("Context for %source is %context", $args) : t("Context for %source is blank", $args));
+ $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? format_string('Context for %source is %context', $args) : format_string('Context for %source is blank', $args));
}
- $this->assertEqual(count($source_strings), count($test_strings), t("Found correct number of source strings."));
+ $this->assertEqual(count($source_strings), count($test_strings), 'Found correct number of source strings.');
}
}
/**
@@ -352,12 +352,12 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
locale_reset();
- $this->assertText($langcode, t('Language code found.'));
- $this->assertText($name, t('Name found.'));
- $this->assertText($native, t('Native found.'));
+ $this->assertText($langcode, 'Language code found.');
+ $this->assertText($name, 'Name found.');
+ $this->assertText($native, 'Native found.');
// No t() here, we do not want to add this string to the database and it's
// surely not translated yet.
- $this->assertText($native, t('Test language added.'));
+ $this->assertText($native, 'Test language added.');
$this->drupalLogout();
// Search for the name and translate it.
@@ -372,8 +372,8 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
// assertText() seems to remove the input field where $name always could be
// found, so this is not a false assert. See how assertNoText succeeds
// later.
- $this->assertText($name, t('Search found the name.'));
- $this->assertRaw($language_indicator, t('Name is untranslated.'));
+ $this->assertText($name, 'Search found the name.');
+ $this->assertRaw($language_indicator, 'Name is untranslated.');
// Assume this is the only result, given the random name.
$this->clickLink(t('edit'));
// We save the lid from the path.
@@ -381,24 +381,24 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches);
$lid = $matches[1];
// No t() here, it's surely not translated yet.
- $this->assertText($name, t('name found on edit screen.'));
+ $this->assertText($name, 'name found on edit screen.');
$edit = array(
"translations[$langcode]" => $translation,
);
$this->drupalPost(NULL, $edit, t('Save translations'));
- $this->assertText(t('The string has been saved.'), t('The string has been saved.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works.'));
+ $this->assertText(t('The string has been saved.'), 'The string has been saved.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, 't() works.');
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// The indicator should not be here.
- $this->assertNoRaw($language_indicator, t('String is translated.'));
+ $this->assertNoRaw($language_indicator, 'String is translated.');
// Try to edit a non-existent string and ensure we're redirected correctly.
// Assuming we don't have 999,999 strings already.
$random_lid = 999999;
$this->drupalGet('admin/config/regional/translate/edit/' . $random_lid);
- $this->assertText(t('String not found'), t('String not found.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertText(t('String not found'), 'String not found.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.');
$this->drupalLogout();
// Delete the language.
@@ -407,11 +407,11 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
// This a confirm form, we do not need any fields changed.
$this->drupalPost($path, array(), t('Delete'));
// We need raw here because %locale will add HTML.
- $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), t('The test language has been removed.'));
+ $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), 'The test language has been removed.');
// Reload to remove $name.
$this->drupalGet($path);
// Verify that language is no longer found.
- $this->assertResponse(404, t('Language no longer found.'));
+ $this->assertResponse(404, 'Language no longer found.');
$this->drupalLogout();
// Delete the string.
@@ -425,20 +425,20 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// Assume this is the only result, given the random name.
$this->clickLink(t('delete'));
- $this->assertText(t('Are you sure you want to delete the string'), t('"delete" link is correct.'));
+ $this->assertText(t('Are you sure you want to delete the string'), '"delete" link is correct.');
// Delete the string.
$path = 'admin/config/regional/translate/delete/' . $lid;
$this->drupalGet($path);
// First test the 'cancel' link.
$this->clickLink(t('Cancel'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertRaw($name, t('The string was not deleted.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertRaw($name, 'The string was not deleted.');
// Delete the name string.
$this->drupalPost('admin/config/regional/translate/delete/' . $lid, array(), t('Delete'));
- $this->assertText(t('The string has been removed.'), t('The string has been removed message.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertText(t('The string has been removed.'), 'The string has been removed message.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.');
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText($name, t('Search now can not find the name.'));
+ $this->assertNoText($name, 'Search now can not find the name.');
}
/*
@@ -494,14 +494,14 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
->execute()
->fetchObject();
$js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/' . $langcode . '_' . $file->javascript . '.js';
- $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('not found'))));
+ $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file created: %file', array('%file' => $result ? $js_file : 'not found')));
// Test JavaScript translation rebuilding.
file_unmanaged_delete($js_file);
- $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
+ $this->assertTrue($result = !file_exists($js_file), format_string('JavaScript file deleted: %file', array('%file' => $result ? $js_file : 'found')));
cache_clear_all();
_locale_rebuild_js($langcode);
- $this->assertTrue($result = file_exists($js_file), t('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : t('not found'))));
+ $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : 'not found')));
}
/**
@@ -554,7 +554,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// Find the edit path.
$content = $this->drupalGetContent();
- $this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), t('Found the edit path.'));
+ $this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), 'Found the edit path.');
$path = $matches[0];
foreach ($bad_translations as $key => $translation) {
$edit = array(
@@ -563,8 +563,8 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
$this->drupalPost($path, $edit, t('Save translations'));
// Check for a form error on the textarea.
$form_class = $this->xpath('//form[@id="locale-translate-edit-form"]//textarea/@class');
- $this->assertNotIdentical(FALSE, strpos($form_class[0], 'error'), t('The string was rejected as unsafe.'));
- $this->assertNoText(t('The string has been saved.'), t('The string was not saved.'));
+ $this->assertNotIdentical(FALSE, strpos($form_class[0], 'error'), 'The string was rejected as unsafe.');
+ $this->assertNoText(t('The string has been saved.'), 'The string was not saved.');
}
}
@@ -621,7 +621,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
// assertText() seems to remove the input field where $name always could be
// found, so this is not a false assert. See how assertNoText succeeds
// later.
- $this->assertText($name, t('Search found the string.'));
+ $this->assertText($name, 'Search found the string.');
// Ensure untranslated string doesn't appear if searching on 'only
// translated strings'.
@@ -632,7 +632,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the string."));
+ $this->assertText(t('No strings available.'), "Search didn't find the string.");
// Ensure untranslated string appears if searching on 'only untranslated
// strings' in "all" (hasn't been translated to any language).
@@ -643,7 +643,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the string.'));
+ $this->assertNoText(t('No strings available.'), '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).
@@ -654,7 +654,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the string.'));
+ $this->assertNoText(t('No strings available.'), 'Search found the string.');
// Add translation.
// Assume this is the only result, given the random name.
@@ -677,7 +677,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the translation.'));
+ $this->assertNoText(t('No strings available.'), 'Search found the translation.');
// Ensure translated source string doesn't appear if searching on 'only
// untranslated strings'.
@@ -688,7 +688,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the source string."));
+ $this->assertText(t('No strings available.'), "Search didn't find the source string.");
// Ensure translated string doesn't appear if searching on 'only
// untranslated strings'.
@@ -699,7 +699,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the translation."));
+ $this->assertText(t('No strings available.'), "Search didn't find the translation.");
// Ensure translated string does appear if searching on the custom language.
$search = array(
@@ -709,7 +709,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the translation.'));
+ $this->assertNoText(t('No strings available.'), 'Search found the translation.');
// Ensure translated string doesn't appear if searching on English.
$search = array(
@@ -719,7 +719,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the translation."));
+ $this->assertText(t('No strings available.'), "Search didn't find the translation.");
// Search for a string that isn't in the system.
$unavailable_string = $this->randomName(16);
@@ -730,7 +730,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the invalid string."));
+ $this->assertText(t('No strings available.'), "Search didn't find the invalid string.");
}
}
@@ -898,16 +898,16 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
));
// The import should automatically create the corresponding language.
- $this->assertRaw(t('The language %language has been created.', array('%language' => 'French')), t('The language has been automatically created.'));
+ $this->assertRaw(t('The language %language has been created.', array('%language' => 'French')), 'The language has been automatically created.');
// The import should have created 7 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 9, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 9, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.');
// This import should have saved plural forms to have 2 variants.
- $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('Plural number initialized.'));
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'Plural number initialized.');
// Ensure we were redirected correctly.
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate', array('absolute' => TRUE)), 'Correct page redirection.');
// Try importing a .po file with invalid tags in the default text group.
@@ -916,9 +916,9 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
));
// The import should have created 1 string and rejected 2.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.');
$skip_message = format_plural(2, 'One translation string was skipped because it contains disallowed HTML.', '@count translation strings were skipped because they contain disallowed HTML.');
- $this->assertRaw($skip_message, t('Unsafe strings were skipped.'));
+ $this->assertRaw($skip_message, 'Unsafe strings were skipped.');
// Try importing a .po file with invalid tags in a non default text group.
@@ -928,7 +928,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
));
// The import should have created 3 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 3, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 3, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.');
// Try importing a .po file which doesn't exist.
@@ -938,8 +938,8 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
'files[file]' => $name,
'group' => 'custom',
), t('Import'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/import', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertText(t('File to import not found.'), t('File to import not found message.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/import', array('absolute' => TRUE)), 'Correct page redirection.');
+ $this->assertText(t('File to import not found.'), 'File to import not found message.');
// Try importing a .po file with overriding strings, and ensure existing
@@ -950,7 +950,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
));
// The import should have created 1 string.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.');
// Ensure string wasn't overwritten.
$search = array(
'string' => 'Montag',
@@ -959,10 +959,10 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t('String not overwritten by imported string.'));
+ $this->assertText(t('No strings available.'), 'String not overwritten by imported string.');
// This import should not have changed number of plural forms.
- $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('Plural numbers untouched.'));
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'Plural numbers untouched.');
$this->importPoFile($this->getPoFileWithBrokenPlural(), array(
'langcode' => 'fr',
@@ -971,7 +971,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
// Attempt to import broken .po file as well to prove that this
// will not overwrite the proper plural formula imported above.
- $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('Broken plurals: plural numbers untouched.'));
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'Broken plurals: plural numbers untouched.');
$this->importPoFile($this->getPoFileWithMissingPlural(), array(
'langcode' => 'fr',
@@ -980,7 +980,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
// Attempt to import .po file which has no plurals and prove that this
// will not overwrite the proper plural formula imported above.
- $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('No plurals: plural numbers untouched.'));
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'No plurals: plural numbers untouched.');
// Try importing a .po file with overriding strings, and ensure existing
@@ -991,7 +991,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
));
// The import should have updated 2 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), 'The translation file was successfully imported.');
// Ensure string was overwritten.
$search = array(
'string' => 'Montag',
@@ -1000,9 +1000,9 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('String overwritten by imported string.'));
+ $this->assertNoText(t('No strings available.'), 'String overwritten by imported string.');
// This import should have changed number of plural forms.
- $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 3, t('Plural numbers changed.'));
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 3, 'Plural numbers changed.');
}
/**
@@ -1031,7 +1031,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
// Ensure the translation file was automatically imported when language was
// added.
- $this->assertText(t('One translation file imported for the enabled modules.'), t('Language file automatically imported.'));
+ $this->assertText(t('One translation file imported for the enabled modules.'), 'Language file automatically imported.');
// Ensure strings were successfully imported.
$search = array(
@@ -1041,7 +1041,7 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
'group' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('String successfully imported.'));
+ $this->assertNoText(t('No strings available.'), 'String successfully imported.');
}
/**
@@ -1053,8 +1053,8 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
'langcode' => 'hr',
));
- $this->assertIdentical(t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', t('Long month name context is working.'));
- $this->assertIdentical(t('May', array(), array('langcode' => 'hr')), 'Svi.', t('Default context is working.'));
+ $this->assertIdentical(t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', 'Long month name context is working.');
+ $this->assertIdentical(t('May', array(), array('langcode' => 'hr')), 'Svi.', 'Default context is working.');
}
/**
@@ -1068,15 +1068,15 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
'langcode' => $langcode,
));
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
- $this->assertIdentical(t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', t('String imported and translated.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.');
+ $this->assertIdentical(t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', 'String imported and translated.');
// Try importing a .po file.
$this->importPoFile($this->getPoFileWithEmptyMsgstr(), array(
'langcode' => $langcode,
'mode' => 0,
));
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 0, '%delete' => 1)), t('The translation file was successfully imported.'));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 0, '%delete' => 1)), 'The translation file was successfully imported.');
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "$langcode ";
@@ -1090,8 +1090,8 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// assertText() seems to remove the input field where $str always could be
// found, so this is not a false assert.
- $this->assertText($str, t('Search found the string.'));
- $this->assertRaw($language_indicator, t('String is untranslated again.'));
+ $this->assertText($str, 'Search found the string.');
+ $this->assertRaw($language_indicator, 'String is untranslated again.');
}
/**
@@ -1202,7 +1202,7 @@ EOF;
* Helper function that returns a .po file with context.
*/
function getPoFileWithContext() {
- // Croatian (code hr) is one the the languages that have a different
+ // Croatian (code hr) is one of the languages that have a different
// form for the full name and the abbreviated name for the month May.
return <<< EOF
msgid ""
@@ -1344,9 +1344,9 @@ class LocaleExportFunctionalTest extends DrupalWebTestCase {
), t('Export'));
// Ensure we have a translation file.
- $this->assertRaw('# French translation of Drupal', t('Exported French translation file.'));
+ $this->assertRaw('# French translation of Drupal', 'Exported French translation file.');
// Ensure our imported translations exist in the file.
- $this->assertRaw('msgstr "lundi"', t('French translations present in exported file.'));
+ $this->assertRaw('msgstr "lundi"', 'French translations present in exported file.');
}
/**
@@ -1359,7 +1359,7 @@ class LocaleExportFunctionalTest extends DrupalWebTestCase {
// doesn't work.
$this->drupalPost('admin/config/regional/translate/export', array(), t('Export'));
// Ensure we have a translation file.
- $this->assertRaw('# LANGUAGE translation of PROJECT', t('Exported translation template file.'));
+ $this->assertRaw('# LANGUAGE translation of PROJECT', 'Exported translation template file.');
}
/**
@@ -1407,7 +1407,7 @@ class LocaleInstallTest extends DrupalWebTestCase {
function testFunctionSignatures() {
$reflector_t = new ReflectionFunction('t');
$reflector_st = new ReflectionFunction('st');
- $this->assertEqual($reflector_t->getParameters(), $reflector_st->getParameters(), t('Function signatures of t() and st() are equal.'));
+ $this->assertEqual($reflector_t->getParameters(), $reflector_st->getParameters(), 'Function signatures of t() and st() are equal.');
}
}
@@ -1446,7 +1446,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
// Check the UI language.
drupal_language_initialize();
global $language;
- $this->assertEqual($language->language, $this->language, t('Current language: %lang', array('%lang' => $language->language)));
+ $this->assertEqual($language->language, $this->language, format_string('Current language: %lang', array('%lang' => $language->language)));
// Enable multilingual workflow option for articles.
variable_set('language_content_type_article', 1);
@@ -1467,7 +1467,7 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
_locale_rebuild_js('fr');
$file = db_query('SELECT javascript FROM {languages} WHERE language = :language', array(':language' => 'fr'))->fetchObject();
$js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $file->javascript . '.js';
- $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('none'))));
+ $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file created: %file', array('%file' => $result ? $js_file : 'none')));
// Disable string caching.
variable_set('locale_cache_strings', 0);
@@ -1492,44 +1492,44 @@ class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
// Check the init language logic.
drupal_language_initialize();
- $this->assertEqual($language->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language->language)));
+ $this->assertEqual($language->language, 'en', format_string('Language after uninstall: %lang', array('%lang' => $language->language)));
// Check JavaScript files deletion.
- $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
+ $this->assertTrue($result = !file_exists($js_file), format_string('JavaScript file deleted: %file', array('%file' => $result ? $js_file : 'found')));
// Check language count.
$language_count = variable_get('language_count', 1);
- $this->assertEqual($language_count, 1, t('Language count: %count', array('%count' => $language_count)));
+ $this->assertEqual($language_count, 1, format_string('Language count: %count', array('%count' => $language_count)));
// Check language negotiation.
require_once DRUPAL_ROOT . '/includes/language.inc';
- $this->assertTrue(count(language_types()) == count(drupal_language_types()), t('Language types reset'));
+ $this->assertTrue(count(language_types()) == count(drupal_language_types()), 'Language types reset');
$language_negotiation = language_negotiation_get(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT;
- $this->assertTrue($language_negotiation, t('Interface language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
+ $this->assertTrue($language_negotiation, format_string('Interface language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
$language_negotiation = language_negotiation_get(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT;
- $this->assertTrue($language_negotiation, t('Content language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
+ $this->assertTrue($language_negotiation, format_string('Content language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
$language_negotiation = language_negotiation_get(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT;
- $this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
+ $this->assertTrue($language_negotiation, format_string('URL language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
// Check language providers settings.
- $this->assertFalse(variable_get('locale_language_negotiation_url_part', FALSE), t('URL language provider indicator settings cleared.'));
- $this->assertFalse(variable_get('locale_language_negotiation_session_param', FALSE), t('Visit language provider settings cleared.'));
+ $this->assertFalse(variable_get('locale_language_negotiation_url_part', FALSE), 'URL language provider indicator settings cleared.');
+ $this->assertFalse(variable_get('locale_language_negotiation_session_param', FALSE), 'Visit language provider settings cleared.');
// Check JavaScript parsed.
$javascript_parsed_count = count(variable_get('javascript_parsed', array()));
- $this->assertEqual($javascript_parsed_count, 0, t('JavaScript parsed count: %count', array('%count' => $javascript_parsed_count)));
+ $this->assertEqual($javascript_parsed_count, 0, format_string('JavaScript parsed count: %count', array('%count' => $javascript_parsed_count)));
// Check multilingual workflow option for articles.
$multilingual = variable_get('language_content_type_article', 0);
- $this->assertEqual($multilingual, 0, t('Multilingual workflow option: %status', array('%status' => t($multilingual ? 'enabled': 'disabled'))));
+ $this->assertEqual($multilingual, 0, format_string('Multilingual workflow option: %status', array('%status' => $multilingual ? 'enabled': 'disabled')));
// Check JavaScript translations directory.
$locale_js_directory = variable_get('locale_js_directory', 'languages');
- $this->assertEqual($locale_js_directory, 'languages', t('JavaScript translations directory: %dir', array('%dir' => $locale_js_directory)));
+ $this->assertEqual($locale_js_directory, 'languages', format_string('JavaScript translations directory: %dir', array('%dir' => $locale_js_directory)));
// Check string caching.
$locale_cache_strings = variable_get('locale_cache_strings', 1);
- $this->assertEqual($locale_cache_strings, 1, t('String caching: %status', array('%status' => t($locale_cache_strings ? 'enabled': 'disabled'))));
+ $this->assertEqual($locale_cache_strings, 1, format_string('String caching: %status', array('%status' => $locale_cache_strings ? 'enabled': 'disabled')));
}
}
@@ -1600,7 +1600,7 @@ class LocaleLanguageSwitchingFunctionalTest extends DrupalWebTestCase {
// Assert that the language switching block is displayed on the frontpage.
$this->drupalGet('');
- $this->assertText(t('Languages'), t('Language switcher block found.'));
+ $this->assertText(t('Languages'), 'Language switcher block found.');
// Assert that only the current language is marked as active.
list($language_switcher) = $this->xpath('//div[@id=:id]/div[@class="content"]', array(':id' => 'block-locale-' . $language_type));
@@ -1629,8 +1629,8 @@ class LocaleLanguageSwitchingFunctionalTest extends DrupalWebTestCase {
$anchors['inactive'][] = $language;
}
}
- $this->assertIdentical($links, array('active' => array('en'), 'inactive' => array('fr')), t('Only the current language list item is marked as active on the language switcher block.'));
- $this->assertIdentical($anchors, array('active' => array('en'), 'inactive' => array('fr')), t('Only the current language anchor is marked as active on the language switcher block.'));
+ $this->assertIdentical($links, array('active' => array('en'), 'inactive' => array('fr')), 'Only the current language list item is marked as active on the language switcher block.');
+ $this->assertIdentical($anchors, array('active' => array('en'), 'inactive' => array('fr')), 'Only the current language anchor is marked as active on the language switcher block.');
}
}
@@ -1685,7 +1685,7 @@ class LocaleBrowserDetectionTest extends DrupalUnitTestCase {
);
$test_cases = array(
- // Equal qvalue for each language, choose the site prefered one.
+ // Equal qvalue for each language, choose the site preferred one.
'en,en-US,fr-CA,fr,es-MX' => 'en',
'en-US,en,fr-CA,fr,es-MX' => 'en',
'fr,en' => 'en',
@@ -1749,7 +1749,7 @@ class LocaleBrowserDetectionTest extends DrupalUnitTestCase {
foreach ($test_cases as $accept_language => $expected_result) {
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = $accept_language;
$result = locale_language_from_browser($languages);
- $this->assertIdentical($result, $expected_result, t("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none')));
+ $this->assertIdentical($result, $expected_result, format_string("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none')));
}
}
}
@@ -1829,21 +1829,21 @@ class LocaleUserLanguageFunctionalTest extends DrupalWebTestCase {
$path = 'user/' . $web_user->uid . '/edit';
$this->drupalGet($path);
// Ensure language settings fieldset is available.
- $this->assertText(t('Language settings'), t('Language settings available.'));
+ $this->assertText(t('Language settings'), 'Language settings available.');
// Ensure custom language is present.
- $this->assertText($name, t('Language present on form.'));
+ $this->assertText($name, 'Language present on form.');
// Ensure disabled language isn't present.
- $this->assertNoText($name_disabled, t('Disabled language not present on form.'));
+ $this->assertNoText($name_disabled, 'Disabled language not present on form.');
// Switch to our custom language.
$edit = array(
'language' => $langcode,
);
$this->drupalPost($path, $edit, t('Save'));
// Ensure form was submitted successfully.
- $this->assertText(t('The changes have been saved.'), t('Changes were saved.'));
+ $this->assertText(t('The changes have been saved.'), 'Changes were saved.');
// Check if language was changed.
$elements = $this->xpath('//input[@id=:id]', array(':id' => 'edit-language-' . $langcode));
- $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), t('Default language successfully updated.'));
+ $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), 'Default language successfully updated.');
$this->drupalLogout();
}
@@ -1881,20 +1881,20 @@ class LocaleUserCreationTest extends DrupalWebTestCase {
'langcode' => 'fr',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
- $this->assertText($langcode, t('Language added successfully.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertText($langcode, 'Language added successfully.');
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
// Set language negotiation.
$edit = array(
'language[enabled][locale-url]' => TRUE,
);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
- $this->assertText(t('Language negotiation configuration saved.'), t('Set language negotiation.'));
+ $this->assertText(t('Language negotiation configuration saved.'), 'Set language negotiation.');
// Check if the language selector is available on admin/people/create and
// set to the currently active language.
$this->drupalGet($langcode . '/admin/people/create');
- $this->assertFieldChecked("edit-language-$langcode", t('Global language set in the language selector.'));
+ $this->assertFieldChecked("edit-language-$langcode", 'Global language set in the language selector.');
// Create a user with the admin/people/create form and check if the correct
// language is set.
@@ -1909,13 +1909,13 @@ class LocaleUserCreationTest extends DrupalWebTestCase {
$this->drupalPost($langcode . '/admin/people/create', $edit, t('Create new account'));
$user = user_load_by_name($username);
- $this->assertEqual($user->language, $langcode, t('New user has correct language set.'));
+ $this->assertEqual($user->language, $langcode, 'New user has correct language set.');
// Register a new user and check if the language selector is hidden.
$this->drupalLogout();
$this->drupalGet($langcode . '/user/register');
- $this->assertNoFieldByName('language[fr]', t('Language selector is not accessible.'));
+ $this->assertNoFieldByName('language[fr]', 'Language selector is not accessible.');
$username = $this->randomName(10);
$edit = array(
@@ -1926,7 +1926,7 @@ class LocaleUserCreationTest extends DrupalWebTestCase {
$this->drupalPost($langcode . '/user/register', $edit, t('Create new account'));
$user = user_load_by_name($username);
- $this->assertEqual($user->language, $langcode, t('New user has correct language set.'));
+ $this->assertEqual($user->language, $langcode, 'New user has correct language set.');
// Test if the admin can use the language selector and if the
// correct language is was saved.
@@ -1934,7 +1934,7 @@ class LocaleUserCreationTest extends DrupalWebTestCase {
$this->drupalLogin($admin_user);
$this->drupalGet($user_edit);
- $this->assertFieldChecked("edit-language-$langcode", t('Language selector is accessible and correct language is selected.'));
+ $this->assertFieldChecked("edit-language-$langcode", 'Language selector is accessible and correct language is selected.');
// Set pass_raw so we can login the new user.
$user->pass_raw = $this->randomName(10);
@@ -1947,7 +1947,7 @@ class LocaleUserCreationTest extends DrupalWebTestCase {
$this->drupalLogin($user);
$this->drupalGet($user_edit);
- $this->assertFieldChecked("edit-language-$langcode", t('Language selector is accessible and correct language is selected.'));
+ $this->assertFieldChecked("edit-language-$langcode", 'Language selector is accessible and correct language is selected.');
}
}
@@ -1999,7 +1999,7 @@ class LocalePathFunctionalTest extends DrupalWebTestCase {
// not enabled yet.
$this->drupalPost('admin/config/regional/language/configure', array(), t('Save settings'));
$this->drupalGet($prefix);
- $this->assertResponse(404, t('The "xx" front page is not available yet.'));
+ $this->assertResponse(404, 'The "xx" front page is not available yet.');
// Enable URL language detection and selection.
$edit = array('language[enabled][locale-url]' => 1);
@@ -2029,11 +2029,11 @@ class LocalePathFunctionalTest extends DrupalWebTestCase {
// Confirm English language path alias works.
$this->drupalGet($english_path);
- $this->assertText($node->title, t('English alias works.'));
+ $this->assertText($node->title, 'English alias works.');
// Confirm custom language path alias works.
$this->drupalGet($prefix . '/' . $custom_language_path);
- $this->assertText($node->title, t('Custom language alias works.'));
+ $this->assertText($node->title, 'Custom language alias works.');
// Create a custom path.
$custom_path = $this->randomName(8);
@@ -2046,10 +2046,10 @@ class LocalePathFunctionalTest extends DrupalWebTestCase {
);
path_save($edit);
$lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, 'en');
- $this->assertEqual($english_path, $lookup_path, t('English language alias has priority.'));
+ $this->assertEqual($english_path, $lookup_path, 'English language alias has priority.');
// Same check for language 'xx'.
$lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, $prefix);
- $this->assertEqual($custom_language_path, $lookup_path, t('Custom language alias has priority.'));
+ $this->assertEqual($custom_language_path, $lookup_path, 'Custom language alias has priority.');
path_delete($edit);
// Create language nodes to check priority of aliases.
@@ -2076,17 +2076,17 @@ class LocalePathFunctionalTest extends DrupalWebTestCase {
$this->drupalGet('');
$custom_path_url = base_path() . (variable_get('clean_url', 0) ? $custom_path : '?q=' . $custom_path);
$elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $first_node->title));
- $this->assertTrue(!empty($elements), t('First node links to the path alias.'));
+ $this->assertTrue(!empty($elements), 'First node links to the path alias.');
$elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $second_node->title));
- $this->assertTrue(!empty($elements), t('Second node links to the path alias.'));
+ $this->assertTrue(!empty($elements), 'Second node links to the path alias.');
// Confirm that the custom path leads to the first node.
$this->drupalGet($custom_path);
- $this->assertText($first_node->title, t('Custom alias returns first node.'));
+ $this->assertText($first_node->title, 'Custom alias returns first node.');
// Confirm that the custom path with prefix leads to the second node.
$this->drupalGet($prefix . '/' . $custom_path);
- $this->assertText($second_node->title, t('Custom alias with prefix returns second node.'));
+ $this->assertText($second_node->title, 'Custom alias with prefix returns second node.');
}
}
@@ -2191,28 +2191,28 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase {
// Set "Basic page" content type to use multilingual support.
$this->drupalGet('admin/structure/types/manage/page');
- $this->assertText(t('Multilingual support'), t('Multilingual support fieldset present on content type configuration form.'));
+ $this->assertText(t('Multilingual support'), 'Multilingual support fieldset present on content type configuration form.');
$edit = array(
'language_content_type' => 1,
);
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
- $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.');
$this->drupalLogout();
// Verify language selection is not present on add article form.
$this->drupalLogin($web_user);
$this->drupalGet('node/add/article');
// Verify language select list is not present.
- $this->assertNoFieldByName('language', NULL, t('Language select not present on add article form.'));
+ $this->assertNoFieldByName('language', NULL, 'Language select not present on add article form.');
// Verify language selection appears on add "Basic page" form.
$this->drupalGet('node/add/page');
// Verify language select list is present.
- $this->assertFieldByName('language', NULL, t('Language select present on add Basic page form.'));
+ $this->assertFieldByName('language', NULL, 'Language select present on add Basic page form.');
// Ensure enabled language appears.
- $this->assertText($name, t('Enabled language present.'));
+ $this->assertText($name, 'Enabled language present.');
// Ensure disabled language doesn't appear.
- $this->assertNoText($name_disabled, t('Disabled language not present.'));
+ $this->assertNoText($name_disabled, 'Disabled language not present.');
// Create "Basic page" content.
$node_title = $this->randomName();
@@ -2227,13 +2227,13 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase {
// Edit the content and ensure correct language is selected.
$path = 'node/' . $node->nid . '/edit';
$this->drupalGet($path);
- $this->assertRaw('', t('Correct language selected.'));
+ $this->assertRaw('', 'Correct language selected.');
// Ensure we can change the node language.
$edit = array(
'language' => 'en',
);
$this->drupalPost($path, $edit, t('Save'));
- $this->assertRaw(t('%title has been updated.', array('%title' => $node_title)), t('Basic page content updated.'));
+ $this->assertRaw(t('%title has been updated.', array('%title' => $node_title)), 'Basic page content updated.');
$this->drupalLogout();
}
@@ -2484,11 +2484,11 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
// language.
$args = array(':url' => base_path() . (!empty($GLOBALS['conf']['clean_url']) ? $language_browser_fallback : "?q=$language_browser_fallback"));
$fields = $this->xpath('//div[@id="block-locale-language"]//a[@class="language-link active" and @href=:url]', $args);
- $this->assertTrue($fields[0] == $languages[$language_browser_fallback]->native, t('The browser language is the URL active language'));
+ $this->assertTrue($fields[0] == $languages[$language_browser_fallback]->native, 'The browser language is the URL active language');
// Check that URLs are rewritten using the given browser language.
$fields = $this->xpath('//div[@id="site-name"]//a[@rel="home" and @href=:url]//span', $args);
- $this->assertTrue($fields[0] == 'Drupal', t('URLs are rewritten using the browser language.'));
+ $this->assertTrue($fields[0] == 'Drupal', 'URLs are rewritten using the browser language.');
}
/**
@@ -2522,13 +2522,13 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
$url = url('admin', array('language' => $languages[$langcode]));
$url_scheme = ($is_https) ? 'https://' : 'http://';
$correct_link = $url_scheme . $link;
- $this->assertTrue($url == $correct_link, t('The url() function returns the right url (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
+ $this->assertTrue($url == $correct_link, format_string('The url() function returns the right url (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
// Test HTTPS via options.
variable_set('https', TRUE);
$url = url('admin', array('https' => TRUE, 'language' => $languages[$langcode]));
$correct_link = 'https://' . $link;
- $this->assertTrue($url == $correct_link, t('The url() function returns the right https url (via options) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
+ $this->assertTrue($url == $correct_link, format_string('The url() function returns the right https url (via options) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
variable_set('https', FALSE);
// Test HTTPS via current URL scheme.
@@ -2536,7 +2536,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
$is_https = TRUE;
$url = url('admin', array('language' => $languages[$langcode]));
$correct_link = 'https://' . $link;
- $this->assertTrue($url == $correct_link, t('The url() function returns the right url (via current url scheme) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
+ $this->assertTrue($url == $correct_link, format_string('The url() function returns the right url (via current url scheme) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
$is_https = $temp_https;
}
}
@@ -2591,13 +2591,13 @@ class LocaleUrlRewritingTest extends DrupalWebTestCase {
function testUrlRewritingEdgeCases() {
// Check URL rewriting with a disabled language.
$languages = language_list();
- $this->checkUrl($languages['it'], t('Path language is ignored if language is disabled.'), t('URL language negotiation does not work with disabled languages'));
+ $this->checkUrl($languages['it'], 'Path language is ignored if language is disabled.', 'URL language negotiation does not work with disabled languages');
// Check URL rewriting with a non-installed language.
$non_existing = language_default();
$non_existing->language = $this->randomName();
$non_existing->prefix = $this->randomName();
- $this->checkUrl($non_existing, t('Path language is ignored if language is not installed.'), t('URL language negotiation does not work with non-installed languages'));
+ $this->checkUrl($non_existing, 'Path language is ignored if language is not installed.', 'URL language negotiation does not work with non-installed languages');
}
/**
@@ -2606,6 +2606,13 @@ class LocaleUrlRewritingTest extends DrupalWebTestCase {
* The test is performed with a fixed URL (the default front page) to simply
* check that language prefixes are not added to it and that the prefixed URL
* is actually not working.
+ *
+ * @param string $language
+ * The language prefix, e.g. 'es'.
+ * @param string $message1
+ * Message to display in assertion that language prefixes are not added.
+ * @param string $message2
+ * The message to display confirming prefixed URL is not working.
*/
private function checkUrl($language, $message1, $message2) {
$options = array('language' => $language);
@@ -2655,7 +2662,7 @@ class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase {
'language_content_type' => 1,
);
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
- $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.');
// Make node body translatable.
$field = field_info_field('body');
@@ -2683,10 +2690,10 @@ class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase {
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
+ $this->assertTrue($node, 'Node found in database.');
$assert = isset($node->body['en']) && !isset($node->body[LANGUAGE_NONE]) && $node->body['en'][0]['value'] == $body_value;
- $this->assertTrue($assert, t('Field language correctly set.'));
+ $this->assertTrue($assert, 'Field language correctly set.');
// Change node language.
$this->drupalGet("node/$node->nid/edit");
@@ -2696,20 +2703,20 @@ class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase {
);
$this->drupalPost(NULL, $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
+ $this->assertTrue($node, 'Node found in database.');
$assert = isset($node->body['it']) && !isset($node->body['en']) && $node->body['it'][0]['value'] == $body_value;
- $this->assertTrue($assert, t('Field language correctly changed.'));
+ $this->assertTrue($assert, 'Field language correctly changed.');
// Enable content language URL detection.
language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LOCALE_LANGUAGE_NEGOTIATION_URL => 0));
// Test multilingual field language fallback logic.
$this->drupalGet("it/node/$node->nid");
- $this->assertRaw($body_value, t('Body correctly displayed using Italian as requested language'));
+ $this->assertRaw($body_value, 'Body correctly displayed using Italian as requested language');
$this->drupalGet("node/$node->nid");
- $this->assertRaw($body_value, t('Body correctly displayed using English as requested language'));
+ $this->assertRaw($body_value, 'Body correctly displayed using English as requested language');
}
/*
@@ -2732,7 +2739,7 @@ class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase {
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
+ $this->assertTrue($node, 'Node found in database.');
// Check if node body is showed.
$this->drupalGet("node/$node->nid");
@@ -2758,7 +2765,7 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase {
parent::setUp('locale', 'locale_test');
// Create and login user.
- $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'administer content types', 'create article content'));
+ $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'administer content types', 'administer comments', 'create article content'));
$this->drupalLogin($admin_user);
// Add language.
@@ -2787,6 +2794,12 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase {
// French no matter what path prefix the URLs have.
$edit = array('language' => 'fr');
$this->drupalPost("user/{$admin_user->uid}/edit", $edit, t('Save'));
+
+ // Make comment body translatable.
+ $field = field_info_field('comment_body');
+ $field['translatable'] = TRUE;
+ field_update_field($field);
+ $this->assertTrue(field_is_translatable('comment', $field), 'Comment body is translatable.');
}
/**
@@ -2817,22 +2830,46 @@ class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase {
foreach (language_list() as $langcode => $language) {
// Post a comment with content language $langcode.
$prefix = empty($language->prefix) ? '' : $language->prefix . '/';
- $edit = array("comment_body[$language_none][0][value]" => $this->randomName());
- $this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Save'));
+ $comment_values[$node_langcode][$langcode] = $this->randomName();
+ // Initially field form widgets have no language.
+ $edit = array(
+ 'subject' => $this->randomName(),
+ "comment_body[$language_none][0][value]" => $comment_values[$node_langcode][$langcode],
+ );
+ $this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Preview'));
+ // After the first submit the submitted entity language is taken into
+ // account.
+ $edit = array(
+ 'subject' => $edit['subject'],
+ "comment_body[$langcode][0][value]" => $comment_values[$node_langcode][$langcode],
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
// Check that comment language matches the current content language.
- $comment = db_select('comment', 'c')
- ->fields('c')
+ $cid = db_select('comment', 'c')
+ ->fields('c', array('cid'))
->condition('nid', $node->nid)
->orderBy('cid', 'DESC')
+ ->range(0, 1)
->execute()
- ->fetchObject();
+ ->fetchField();
+ $comment = comment_load($cid);
$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));
+ $this->assertEqual($comment_langcode, $langcode, format_string('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
+ $this->assertEqual($comment->comment_body[$langcode][0]['value'], $comment_values[$node_langcode][$langcode], 'Comment body correctly stored.');
+ }
+ }
+
+ // Check that comment bodies appear in the administration UI.
+ $this->drupalGet('admin/content/comment');
+ foreach ($comment_values as $node_values) {
+ foreach ($node_values as $value) {
+ $this->assertRaw($value);
}
}
}
+
}
/**
@@ -2897,10 +2934,10 @@ class LocaleDateFormatsFunctionalTest extends DrupalWebTestCase {
// Configure format for the node posted date changes with the language.
$this->drupalGet('node/' . $node->nid);
$english_date = format_date($node->created, 'custom', 'j M Y');
- $this->assertText($english_date, t('English date format appears'));
+ $this->assertText($english_date, 'English date format appears');
$this->drupalGet('fr/node/' . $node->nid);
$french_date = format_date($node->created, 'custom', 'd.m.Y');
- $this->assertText($french_date, t('French date format appears'));
+ $this->assertText($french_date, 'French date format appears');
}
}
@@ -2944,7 +2981,7 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
$this->languageNegotiationUpdate();
$type = LANGUAGE_TYPE_CONTENT;
$language_types = variable_get('language_types', drupal_language_types());
- $this->assertTrue($language_types[$type], t('Content language type is configurable.'));
+ $this->assertTrue($language_types[$type], 'Content language type is configurable.');
// Enable some core and custom language providers. The test language type is
// supposed to be configurable.
@@ -2964,18 +3001,18 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
variable_set('locale_test_language_negotiation_info_alter', TRUE);
$this->languageNegotiationUpdate();
$negotiation = variable_get("language_negotiation_$type", array());
- $this->assertFalse(isset($negotiation[$provider]), t('Interface language provider removed from the stored settings.'));
- $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, t('Interface language provider unavailable.'));
+ $this->assertFalse(isset($negotiation[$provider]), 'Interface language provider removed from the stored settings.');
+ $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, 'Interface language provider unavailable.');
// Check that type-specific language providers can be assigned only to the
// corresponding language types.
foreach (language_types_configurable() as $type) {
$form_field = $type . '[enabled][test_language_provider_ts]';
if ($type == $test_type) {
- $this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, t('Type-specific test language provider available for %type.', array('%type' => $type)));
+ $this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language provider available for %type.', array('%type' => $type)));
}
else {
- $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, t('Type-specific test language provider unavailable for %type.', array('%type' => $type)));
+ $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language provider unavailable for %type.', array('%type' => $type)));
}
}
@@ -2985,7 +3022,7 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
foreach (language_types() as $type) {
$langcode = $last[$type];
$value = $type == LANGUAGE_TYPE_CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en';
- $this->assertEqual($langcode, $value, t('The negotiated language for %type is %language', array('%type' => $type, '%language' => $langcode)));
+ $this->assertEqual($langcode, $value, format_string('The negotiated language for %type is %language', array('%type' => $type, '%language' => $langcode)));
}
// Disable locale_test and check that everything is set back to the original
@@ -2994,7 +3031,7 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
// Check that only the core language types are available.
foreach (language_types() as $type) {
- $this->assertTrue(strpos($type, 'test') === FALSE, t('The %type language is still available', array('%type' => $type)));
+ $this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type)));
}
// Check that fixed language types are properly configured, even those
@@ -3004,11 +3041,11 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
// Check that unavailable language providers are not present in the
// negotiation settings.
$negotiation = variable_get("language_negotiation_$type", array());
- $this->assertFalse(isset($negotiation[$test_provider]), t('The disabled test language provider is not part of the content language negotiation settings.'));
+ $this->assertFalse(isset($negotiation[$test_provider]), 'The disabled test language provider is not part of the content language negotiation settings.');
// Check that configuration page presents the correct options and settings.
- $this->assertNoRaw(t('Test language detection'), t('No test language type configuration available.'));
- $this->assertNoRaw(t('This is a test language provider'), t('No test language provider available.'));
+ $this->assertNoRaw(t('Test language detection'), 'No test language type configuration available.');
+ $this->assertNoRaw(t('This is a test language provider'), 'No test language provider available.');
}
/**
@@ -3053,9 +3090,54 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
list(, $info_id) = each($info['fixed']);
$equal = $info_id == $id;
}
- $this->assertTrue($equal, t('language negotiation for %type is properly set up', array('%type' => $type)));
+ $this->assertTrue($equal, format_string('language negotiation for %type is properly set up', array('%type' => $type)));
}
}
}
}
+/**
+ * Functional tests for CSS alter functions.
+ */
+class LocaleCSSAlterTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'CSS altering',
+ 'description' => 'Test CSS alter functions.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+ }
+
+ /**
+ * Verifies that -rtl.css file is added directly after LTR .css file.
+ */
+ function testCSSFilesOrderInRTLMode() {
+ global $base_url;
+
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
+
+ // Log in as admin.
+ $this->drupalLogin($admin_user);
+
+ // Install the Arabic language (which is RTL) and configure as the default.
+ $edit = array();
+ $edit['langcode'] = 'ar';
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ $edit = array();
+ $edit['site_default'] = 'ar';
+ $this->drupalPost(NULL, $edit, t('Save configuration'));
+
+ // Verify that the -rtl.css file is added directly after LTR file.
+ $this->drupalGet('');
+ $query_string = '?' . variable_get('css_js_query_string', '0');
+ $this->assertRaw('@import url("' . $base_url . '/modules/system/system.base.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.base-rtl.css' . $query_string . '");' . "\n", 'CSS: system.base-rtl.css is added directly after system.base.css.');
+ $this->assertRaw('@import url("' . $base_url . '/modules/system/system.menus.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.menus-rtl.css' . $query_string . '");' . "\n", 'CSS: system.menus-rtl.css is added directly after system.menus.css.');
+ $this->assertRaw('@import url("' . $base_url . '/modules/system/system.messages.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.messages-rtl.css' . $query_string . '");' . "\n", 'CSS: system.messages-rtl.css is added directly after system.messages.css.');
+ }
+}
diff --git a/modules/locale/tests/locale_test.info b/modules/locale/tests/locale_test.info
index 3744a9e..5391d65 100644
--- a/modules/locale/tests/locale_test.info
+++ b/modules/locale/tests/locale_test.info
@@ -4,3 +4,9 @@ core = 7.x
package = Testing
version = VERSION
hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc
index 5ac755e..66bd6f3 100644
--- a/modules/menu/menu.admin.inc
+++ b/modules/menu/menu.admin.inc
@@ -395,7 +395,7 @@ function menu_edit_item_validate($form, &$form_state) {
else {
unset($item['options']['fragment']);
}
- if ($item['link_path'] != $parsed_link['path']) {
+ if (isset($parsed_link['path']) && $item['link_path'] != $parsed_link['path']) {
$item['link_path'] = $parsed_link['path'];
}
}
@@ -512,8 +512,7 @@ function menu_delete_menu_page($menu) {
// System-defined menus may not be deleted.
$system_menus = menu_list_system_menus();
if (isset($system_menus[$menu['menu_name']])) {
- drupal_access_denied();
- return;
+ return MENU_ACCESS_DENIED;
}
return drupal_get_form('menu_delete_menu_confirm', $menu);
}
@@ -622,8 +621,7 @@ function menu_item_delete_page($item) {
// Links defined via hook_menu may not be deleted. Updated items are an
// exception, as they can be broken.
if ($item['module'] == 'system' && !$item['updated']) {
- drupal_access_denied();
- return;
+ return MENU_ACCESS_DENIED;
}
return drupal_get_form('menu_item_delete_form', $item);
}
diff --git a/modules/menu/menu.api.php b/modules/menu/menu.api.php
index 3f3818e..22d93ef 100644
--- a/modules/menu/menu.api.php
+++ b/modules/menu/menu.api.php
@@ -11,7 +11,7 @@
*/
/**
- * Informs modules that a custom menu was created.
+ * Respond to a custom menu creation.
*
* This hook is used to notify modules that a custom menu has been created.
* Contributed modules may use the information to perform actions based on the
@@ -34,7 +34,7 @@ function hook_menu_insert($menu) {
}
/**
- * Informs modules that a custom menu was updated.
+ * Respond to a custom menu update.
*
* This hook is used to notify modules that a custom menu has been updated.
* Contributed modules may use the information to perform actions based on the
@@ -59,14 +59,14 @@ function hook_menu_update($menu) {
}
/**
- * Informs modules that a custom menu was deleted.
+ * Respond to a custom menu deletion.
*
* This hook is used to notify modules that a custom menu along with all links
* contained in it (if any) has been deleted. Contributed modules may use the
* information to perform actions based on the information entered into the menu
* system.
*
- * @param $link
+ * @param $menu
* An array representing a custom menu:
* - menu_name: The unique name of the custom menu.
* - title: The human readable menu title.
diff --git a/modules/menu/menu.info b/modules/menu/menu.info
index 2219f69..4c8f89f 100644
--- a/modules/menu/menu.info
+++ b/modules/menu/menu.info
@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
files[] = menu.test
configure = admin/structure/menu
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/menu/menu.module b/modules/menu/menu.module
index 6444791..dc8f015 100644
--- a/modules/menu/menu.module
+++ b/modules/menu/menu.module
@@ -69,7 +69,7 @@ function menu_menu() {
'title' => 'Parent menu items',
'page callback' => 'menu_parent_options_js',
'type' => MENU_CALLBACK,
- 'access arguments' => array(TRUE),
+ 'access arguments' => array('administer menu'),
);
$items['admin/structure/menu/list'] = array(
'title' => 'List menus',
diff --git a/modules/menu/menu.test b/modules/menu/menu.test
index 324ba67..a9bdb5f 100644
--- a/modules/menu/menu.test
+++ b/modules/menu/menu.test
@@ -70,7 +70,7 @@ class MenuTestCase extends DrupalWebTestCase {
$item['options']['attributes']['title'] = $description;
menu_link_save($item);
$saved_item = menu_link_load($item['mlid']);
- $this->assertEqual($description, $saved_item['options']['attributes']['title'], t('Saving an existing link updates the description (title attribute)'));
+ $this->assertEqual($description, $saved_item['options']['attributes']['title'], 'Saving an existing link updates the description (title attribute)');
$this->resetMenuLink($item, $old_title);
}
@@ -111,14 +111,14 @@ class MenuTestCase extends DrupalWebTestCase {
// Assert the new menu.
$this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
- $this->assertRaw($title, t('Custom menu was added.'));
+ $this->assertRaw($title, 'Custom menu was added.');
// Edit the menu.
$new_title = $this->randomName(16);
$menu['title'] = $new_title;
menu_save($menu);
$this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
- $this->assertRaw($new_title, t('Custom menu was edited.'));
+ $this->assertRaw($new_title, 'Custom menu was edited.');
}
/**
@@ -167,7 +167,7 @@ class MenuTestCase extends DrupalWebTestCase {
$edit['blocks[menu_' . $menu_name . '][region]'] = 'sidebar_first';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertResponse(200);
- $this->assertText(t('The block settings have been updated.'), t('Custom menu block was enabled'));
+ $this->assertText(t('The block settings have been updated.'), 'Custom menu block was enabled');
return menu_load($menu_name);
}
@@ -184,11 +184,11 @@ class MenuTestCase extends DrupalWebTestCase {
// Delete custom menu.
$this->drupalPost("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
$this->assertResponse(200);
- $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $title)), t('Custom menu was deleted'));
+ $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $title)), 'Custom menu was deleted');
$this->assertFalse(menu_load($menu_name), 'Custom menu was deleted');
// Test if all menu links associated to the menu were removed from database.
$result = db_query("SELECT menu_name FROM {menu_links} WHERE menu_name = :menu_name", array(':menu_name' => $menu_name))->fetchField();
- $this->assertFalse($result, t('All menu links associated to the custom menu were deleted.'));
+ $this->assertFalse($result, 'All menu links associated to the custom menu were deleted.');
}
/**
@@ -266,13 +266,13 @@ class MenuTestCase extends DrupalWebTestCase {
$item = $this->addMenuLink(0, $path);
$this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
- $this->assertFieldByName('link_path', $path, t('Path is found with both query and fragment.'));
+ $this->assertFieldByName('link_path', $path, 'Path is found with both query and fragment.');
// Now change the path to something without query and fragment.
$path = 'node';
$this->drupalPost('admin/structure/menu/item/' . $item['mlid'] . '/edit', array('link_path' => $path), t('Save'));
$this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
- $this->assertFieldByName('link_path', $path, t('Path no longer has query or fragment.'));
+ $this->assertFieldByName('link_path', $path, 'Path no longer has query or fragment.');
}
/**
@@ -318,7 +318,7 @@ class MenuTestCase extends DrupalWebTestCase {
* @param string $menu_name Menu name.
*/
function addInvalidMenuLink($menu_name = 'navigation') {
- foreach (array('-&-', 'admin/people/permissions') as $link_path) {
+ foreach (array('-&-', 'admin/people/permissions', '#') as $link_path) {
$edit = array(
'link_path' => $link_path,
'link_title' => 'title',
@@ -350,7 +350,7 @@ class MenuTestCase extends DrupalWebTestCase {
// Verify menu link link.
$this->clickLink($title);
$title = $parent_node->title;
- $this->assertTitle(t("@title | Drupal", array('@title' => $title)), t('Parent menu link link target was correct'));
+ $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Parent menu link link target was correct');
}
// Verify menu link.
@@ -360,7 +360,7 @@ class MenuTestCase extends DrupalWebTestCase {
// Verify menu link link.
$this->clickLink($title);
$title = $item_node->title;
- $this->assertTitle(t("@title | Drupal", array('@title' => $title)), t('Menu link link target was correct'));
+ $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Menu link link target was correct');
}
/**
@@ -412,7 +412,7 @@ class MenuTestCase extends DrupalWebTestCase {
// Reset menu link.
$this->drupalPost("admin/structure/menu/item/$mlid/reset", array(), t('Reset'));
$this->assertResponse(200);
- $this->assertRaw(t('The menu link was reset to its default settings.'), t('Menu link was reset'));
+ $this->assertRaw(t('The menu link was reset to its default settings.'), 'Menu link was reset');
// Verify menu link.
$this->drupalGet('');
@@ -432,7 +432,7 @@ class MenuTestCase extends DrupalWebTestCase {
// Delete menu link.
$this->drupalPost("admin/structure/menu/item/$mlid/delete", array(), t('Confirm'));
$this->assertResponse(200);
- $this->assertRaw(t('The menu link %title has been deleted.', array('%title' => $title)), t('Menu link was deleted'));
+ $this->assertRaw(t('The menu link %title has been deleted.', array('%title' => $title)), 'Menu link was deleted');
// Verify deletion.
$this->drupalGet('');
@@ -509,10 +509,27 @@ class MenuTestCase extends DrupalWebTestCase {
$item['link_path'] .= '#' . $options['fragment'];
}
foreach ($expected_item as $key => $value) {
- $this->assertEqual($item[$key], $value, t('Parameter %key had expected value.', array('%key' => $key)));
+ $this->assertEqual($item[$key], $value, format_string('Parameter %key had expected value.', array('%key' => $key)));
}
}
+ /**
+ * Test administrative users other than user 1 can access the menu parents AJAX callback.
+ */
+ public function testMenuParentsJsAccess() {
+ $admin = $this->drupalCreateUser(array('administer menu'));
+ $this->drupalLogin($admin);
+ // Just check access to the callback overall, the POST data is irrelevant.
+ $this->drupalGetAJAX('admin/structure/menu/parents');
+ $this->assertResponse(200);
+
+ // Do standard user tests.
+ // Login the user.
+ $this->drupalLogin($this->std_user);
+ $this->drupalGetAJAX('admin/structure/menu/parents');
+ $this->assertResponse(403);
+ }
+
/**
* Get standard menu link.
*/
@@ -537,21 +554,21 @@ class MenuTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/help/menu');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertText(t('Menu'), t('Menu help was displayed'));
+ $this->assertText(t('Menu'), 'Menu help was displayed');
}
// View menu build overview node.
$this->drupalGet('admin/structure/menu');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertText(t('Menus'), t('Menu build overview node was displayed'));
+ $this->assertText(t('Menus'), 'Menu build overview node was displayed');
}
// View navigation menu customization node.
$this->drupalGet('admin/structure/menu/manage/navigation');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertText(t('Navigation'), t('Navigation menu node was displayed'));
+ $this->assertText(t('Navigation'), 'Navigation menu node was displayed');
}
// View menu edit node.
@@ -559,21 +576,21 @@ class MenuTestCase extends DrupalWebTestCase {
$this->drupalGet('admin/structure/menu/item/' . $item['mlid'] . '/edit');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertText(t('Edit menu item'), t('Menu edit node was displayed'));
+ $this->assertText(t('Edit menu item'), 'Menu edit node was displayed');
}
// View menu settings node.
$this->drupalGet('admin/structure/menu/settings');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertText(t('Menus'), t('Menu settings node was displayed'));
+ $this->assertText(t('Menus'), 'Menu settings node was displayed');
}
// View add menu node.
$this->drupalGet('admin/structure/menu/add');
$this->assertResponse($response);
if ($response == 200) {
- $this->assertText(t('Menus'), t('Add menu node was displayed'));
+ $this->assertText(t('Menus'), 'Add menu node was displayed');
}
}
}
@@ -654,7 +671,7 @@ class MenuNodeTestCase extends DrupalWebTestCase {
$this->assertLink($node_title);
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertOptionSelected('edit-menu-weight', 17, t('Menu weight correct in edit form'));
+ $this->assertOptionSelected('edit-menu-weight', 17, 'Menu weight correct in edit form');
// Edit the node and remove the menu link.
$edit = array(
@@ -675,11 +692,11 @@ class MenuNodeTestCase extends DrupalWebTestCase {
// Assert that disabled Management menu is not shown on the node/$nid/edit page.
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertText('Provide a menu link', t('Link in not allowed menu not shown in node edit form'));
+ $this->assertText('Provide a menu link', 'Link in not allowed menu not shown in node edit form');
// Assert that the link is still in the management menu after save.
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$link = menu_link_load($item['mlid']);
- $this->assertTrue($link, t('Link in not allowed menu still exists after saving node'));
+ $this->assertTrue($link, 'Link in not allowed menu still exists after saving node');
// Move the menu link back to the Navigation menu.
$item['menu_name'] = 'navigation';
diff --git a/modules/node/content_types.inc b/modules/node/content_types.inc
index 72adc34..55af667 100644
--- a/modules/node/content_types.inc
+++ b/modules/node/content_types.inc
@@ -2,7 +2,7 @@
/**
* @file
- * Content type editing UI.
+ * Content type editing user interface.
*/
/**
@@ -255,11 +255,11 @@ function _node_characters($length) {
*/
function node_type_form_validate($form, &$form_state) {
$type = new stdClass();
- $type->type = trim($form_state['values']['type']);
+ $type->type = $form_state['values']['type'];
$type->name = trim($form_state['values']['name']);
// Work out what the type was before the user submitted this form
- $old_type = trim($form_state['values']['old_type']);
+ $old_type = $form_state['values']['old_type'];
$types = node_type_get_names();
@@ -288,7 +288,7 @@ function node_type_form_submit($form, &$form_state) {
$type = node_type_set_defaults();
- $type->type = trim($form_state['values']['type']);
+ $type->type = $form_state['values']['type'];
$type->name = trim($form_state['values']['name']);
$type->orig_type = trim($form_state['values']['orig_type']);
$type->old_type = isset($form_state['values']['old_type']) ? $form_state['values']['old_type'] : $type->type;
@@ -388,8 +388,7 @@ function node_node_type_update($info) {
}
/**
- * Resets all of the relevant fields of a module-defined node type to their
- * default values.
+ * Resets relevant fields of a module-defined node type to their default values.
*
* @param $type
* The node type to reset. The node type is passed back by reference with its
@@ -410,6 +409,8 @@ function node_type_reset($type) {
/**
* Menu callback; delete a single content type.
+ *
+ * @ingroup forms
*/
function node_type_delete_confirm($form, &$form_state, $type) {
$form['type'] = array('#type' => 'value', '#value' => $type->type);
@@ -430,6 +431,8 @@ function node_type_delete_confirm($form, &$form_state, $type) {
/**
* Process content type delete confirm submissions.
+ *
+ * @see node_type_delete_confirm()
*/
function node_type_delete_confirm_submit($form, &$form_state) {
node_type_delete($form_state['values']['type']);
diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc
index 1508bc0..35f4c1d 100644
--- a/modules/node/node.admin.inc
+++ b/modules/node/node.admin.inc
@@ -7,6 +7,10 @@
/**
* Menu callback: confirm rebuilding of permissions.
+ *
+ * @see node_configure_rebuild_confirm_submit()
+ * @see node_menu()
+ * @ingroup forms
*/
function node_configure_rebuild_confirm() {
return confirm_form(array(), t('Are you sure you want to rebuild the permissions on site content?'),
@@ -15,6 +19,8 @@ function node_configure_rebuild_confirm() {
/**
* Handler for wipe confirmation
+ *
+ * @see node_configure_rebuild_confirm()
*/
function node_configure_rebuild_confirm_submit($form, &$form_state) {
node_access_rebuild(TRUE);
@@ -66,6 +72,9 @@ function node_node_operations() {
/**
* List node administration filters that can be applied.
+ *
+ * @return
+ * An associative array of filters.
*/
function node_filters() {
// Regular filters
@@ -110,7 +119,7 @@ function node_filters() {
}
/**
- * Apply filters for node administration filters based on session.
+ * Applies filters for node administration filters based on session.
*
* @param $query
* A SelectQuery to which the filters should be applied.
@@ -133,7 +142,16 @@ function node_build_filter_query(SelectQueryInterface $query) {
}
/**
- * Return form for node administration filters.
+ * Returns the node administration filters form array to node_admin_content().
+ *
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_submit()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form_submit()
+ * @see node_multiple_delete_confirm()
+ * @see node_multiple_delete_confirm_submit()
+ *
+ * @ingroup forms
*/
function node_filter_form() {
$session = isset($_SESSION['node_overview_filter']) ? $_SESSION['node_overview_filter'] : array();
@@ -208,7 +226,15 @@ function node_filter_form() {
}
/**
- * Process result from node administration filter form.
+ * Form submission handler for node_filter_form().
+ *
+ * @see node_admin_content()
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_submit()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form()
+ * @see node_multiple_delete_confirm()
+ * @see node_multiple_delete_confirm_submit()
*/
function node_filter_form_submit($form, &$form_state) {
$filters = node_filters();
@@ -240,15 +266,15 @@ function node_filter_form_submit($form, &$form_state) {
* Make mass update of nodes, changing all nodes in the $nodes array
* to update them with the field values in $updates.
*
- * IMPORTANT NOTE: This function is intended to work when called
- * from a form submit handler. Calling it outside of the form submission
- * process may not work correctly.
+ * IMPORTANT NOTE: This function is intended to work when called from a form
+ * submission handler. Calling it outside of the form submission process may not
+ * work correctly.
*
* @param array $nodes
* Array of node nids to update.
* @param array $updates
- * Array of key/value pairs with node field names and the
- * value to update that field to.
+ * Array of key/value pairs with node field names and the value to update that
+ * field to.
*/
function node_mass_update($nodes, $updates) {
// We use batch processing to prevent timeout when updating a large number
@@ -279,7 +305,17 @@ function node_mass_update($nodes, $updates) {
}
/**
- * Node Mass Update - helper function.
+ * Updates individual nodes when fewer than 10 are queued.
+ *
+ * @param $nid
+ * ID of node to update.
+ * @param $updates
+ * Associative array of updates.
+ *
+ * @return object
+ * An updated node object.
+ *
+ * @see node_mass_update()
*/
function _node_mass_update_helper($nid, $updates) {
$node = node_load($nid, NULL, TRUE);
@@ -293,7 +329,14 @@ function _node_mass_update_helper($nid, $updates) {
}
/**
- * Node Mass Update Batch operation
+ * Executes a batch operation for node_mass_update().
+ *
+ * @param array $nodes
+ * An array of node IDs.
+ * @param array $updates
+ * Associative array of updates.
+ * @param array $context
+ * An array of contextual key/values.
*/
function _node_mass_update_batch_process($nodes, $updates, &$context) {
if (!isset($context['sandbox']['progress'])) {
@@ -324,7 +367,15 @@ function _node_mass_update_batch_process($nodes, $updates, &$context) {
}
/**
- * Node Mass Update Batch 'finished' callback.
+ * Menu callback: Reports the status of batch operation for node_mass_update().
+ *
+ * @param bool $success
+ * A boolean indicating whether the batch mass update operation successfully
+ * concluded.
+ * @param int $results
+ * The number of nodes updated via the batch mode process.
+ * @param array $operations
+ * An array of function calls (not used in this function).
*/
function _node_mass_update_batch_finished($success, $results, $operations) {
if ($success) {
@@ -339,7 +390,17 @@ function _node_mass_update_batch_finished($success, $results, $operations) {
}
/**
- * Menu callback: content administration.
+ * Page callback: Form constructor for the content administration form.
+ *
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_submit()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form()
+ * @see node_filter_form_submit()
+ * @see node_menu()
+ * @see node_multiple_delete_confirm()
+ * @see node_multiple_delete_confirm_submit()
+ * @ingroup forms
*/
function node_admin_content($form, $form_state) {
if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
@@ -354,6 +415,15 @@ function node_admin_content($form, $form_state) {
/**
* Form builder: Builds the node administration overview.
+ *
+ * @see node_admin_nodes_submit()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form()
+ * @see node_filter_form_submit()
+ * @see node_multiple_delete_confirm()
+ * @see node_multiple_delete_confirm_submit()
+ *
+ * @ingroup forms
*/
function node_admin_nodes() {
$admin_access = user_access('administer nodes');
@@ -401,6 +471,7 @@ function node_admin_nodes() {
$header['operations'] = array('data' => t('Operations'));
$query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
+ $query->addTag('node_admin_filter');
node_build_filter_query($query);
if (!user_access('bypass node access')) {
@@ -525,8 +596,15 @@ function node_admin_nodes() {
/**
* Validate node_admin_nodes form submissions.
*
- * Check if any nodes have been selected to perform the chosen
- * 'Update option' on.
+ * Checks whether any nodes have been selected to perform the chosen 'Update
+ * option' on.
+ *
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_submit()
+ * @see node_filter_form()
+ * @see node_filter_form_submit()
+ * @see node_multiple_delete_confirm()
+ * @see node_multiple_delete_confirm_submit()
*/
function node_admin_nodes_validate($form, &$form_state) {
// Error if there are no items to select.
@@ -538,7 +616,14 @@ function node_admin_nodes_validate($form, &$form_state) {
/**
* Process node_admin_nodes form submissions.
*
- * Execute the chosen 'Update option' on the selected nodes.
+ * Executes the chosen 'Update option' on the selected nodes.
+ *
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form()
+ * @see node_filter_form_submit()
+ * @see node_multiple_delete_confirm()
+ * @see node_multiple_delete_confirm_submit()
*/
function node_admin_nodes_submit($form, &$form_state) {
$operations = module_invoke_all('node_operations');
@@ -564,6 +649,17 @@ function node_admin_nodes_submit($form, &$form_state) {
}
}
+/**
+ * Multiple node deletion confirmation form for node_admin_content().
+ *
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_submit()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form()
+ * @see node_filter_form_submit()
+ * @see node_multiple_delete_confirm_submit()
+ * @ingroup forms
+ */
function node_multiple_delete_confirm($form, &$form_state, $nodes) {
$form['nodes'] = array('#prefix' => '', '#suffix' => '
', '#tree' => TRUE);
// array_filter returns only elements with TRUE values
@@ -587,9 +683,20 @@ function node_multiple_delete_confirm($form, &$form_state, $nodes) {
t('Delete'), t('Cancel'));
}
+/**
+ * Form submission handler for node_multiple_delete_confirm().
+ *
+ * @see node_admin_nodes()
+ * @see node_admin_nodes_submit()
+ * @see node_admin_nodes_validate()
+ * @see node_filter_form()
+ * @see node_filter_form_submit()
+ * @see node_multiple_delete_confirm()
+ */
function node_multiple_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
node_delete_multiple(array_keys($form_state['values']['nodes']));
+ cache_clear_all();
$count = count($form_state['values']['nodes']);
watchdog('content', 'Deleted @count posts.', array('@count' => $count));
drupal_set_message(format_plural($count, 'Deleted 1 post.', 'Deleted @count posts.'));
diff --git a/modules/node/node.api.php b/modules/node/node.api.php
index 052effc..9a4d095 100644
--- a/modules/node/node.api.php
+++ b/modules/node/node.api.php
@@ -11,21 +11,24 @@
* Functions to define and modify content types.
*
* Each content type is maintained by a primary module, which is either
- * node.module (for content types created in the user interface) or the
- * module that implements hook_node_info() to define the content type.
+ * node.module (for content types created in the user interface) or the module
+ * that implements hook_node_info() to define the content type.
*
* During node operations (create, update, view, delete, etc.), there are
* several sets of hooks that get invoked to allow modules to modify the base
* node operation:
- * - Node-type-specific hooks: These hooks are only invoked on the primary
- * module, using the "base" return component of hook_node_info() as the
- * function prefix. For example, poll.module defines the base for the Poll
- * content type as "poll", so during creation of a poll node, hook_insert() is
- * only invoked by calling poll_insert().
- * - All-module hooks: This set of hooks is invoked on all implementing
- * modules, to allow other modules to modify what the primary node module is
- * doing. For example, hook_node_insert() is invoked on all modules when
- * creating a poll node.
+ * - Node-type-specific hooks: When defining a node type, hook_node_info()
+ * returns a 'base' component. Node-type-specific hooks are named
+ * base_hookname() instead of mymodule_hookname() (in a module called
+ * 'mymodule' for example). Only the node type's corresponding implementation
+ * is invoked. For example, poll_node_info() in poll.module defines the base
+ * for the 'poll' node type as 'poll'. So when a poll node is created,
+ * hook_insert() is invoked on poll_insert() only.
+ * Hooks that are node-type-specific are noted below.
+ * - All-module hooks: This set of hooks is invoked on all implementing modules,
+ * to allow other modules to modify what the primary node module is doing. For
+ * example, hook_node_insert() is invoked on all modules when creating a poll
+ * node.
* - Field hooks: Hooks related to the fields attached to the node. These are
* invoked from the field operations functions described below, and can be
* either field-type-specific or all-module hooks.
@@ -56,16 +59,15 @@
* - hook_entity_update() (all)
* - hook_node_access_records() (all)
* - hook_node_access_records_alter() (all)
- * - Loading a node (calling node_load(), node_load_multiple(), or
- * entity_load() with $entity_type of 'node'):
+ * - Loading a node (calling node_load(), node_load_multiple() or entity_load()
+ * with $entity_type of 'node'):
* - Node and revision information is read from database.
* - hook_load() (node-type-specific)
* - field_attach_load_revision() and field_attach_load()
* - hook_entity_load() (all)
* - hook_node_load() (all)
* - Viewing a single node (calling node_view() - note that the input to
- * node_view() is a loaded node, so the Loading steps above are already
- * done):
+ * node_view() is a loaded node, so the Loading steps above are already done):
* - hook_view() (node-type-specific)
* - field_attach_prepare_view()
* - hook_entity_prepare_view() (all)
@@ -97,9 +99,8 @@
* - Revision information is deleted from database
* - hook_node_revision_delete() (all)
* - field_attach_delete_revision()
- * - Preparing a node for editing (calling node_form() - note that if it's
- * an existing node, it will already be loaded; see the Loading section
- * above):
+ * - Preparing a node for editing (calling node_form() - note that if it is an
+ * existing node, it will already be loaded; see the Loading section above):
* - hook_prepare() (node-type-specific)
* - hook_node_prepare() (all)
* - hook_form() (node-type-specific)
@@ -137,16 +138,16 @@
* associated with permission to view, edit, and delete individual nodes.
*
* The realms and grant IDs can be arbitrarily defined by your node access
- * module; it is common to use role IDs as grant IDs, but that is not
- * required. Your module could instead maintain its own list of users, where
- * each list has an ID. In that case, the return value of this hook would be
- * an array of the list IDs that this user is a member of.
+ * module; it is common to use role IDs as grant IDs, but that is not required.
+ * Your module could instead maintain its own list of users, where each list has
+ * an ID. In that case, the return value of this hook would be an array of the
+ * list IDs that this user is a member of.
*
- * A node access module may implement as many realms as necessary to
- * properly define the access privileges for the nodes. Note that the system
- * makes no distinction between published and unpublished nodes. It is the
- * module's responsibility to provide appropriate realms to limit access to
- * unpublished content.
+ * A node access module may implement as many realms as necessary to properly
+ * define the access privileges for the nodes. Note that the system makes no
+ * distinction between published and unpublished nodes. It is the module's
+ * responsibility to provide appropriate realms to limit access to unpublished
+ * content.
*
* Node access records are stored in the {node_access} table and define which
* grants are required to access a node. There is a special case for the view
@@ -183,7 +184,7 @@
* @param $account
* The user object whose grants are requested.
* @param $op
- * The node operation to be performed, such as "view", "update", or "delete".
+ * The node operation to be performed, such as 'view', 'update', or 'delete'.
*
* @return
* An array whose keys are "realms" of grants, and whose values are arrays of
@@ -197,7 +198,7 @@ function hook_node_grants($account, $op) {
if (user_access('access private content', $account)) {
$grants['example'] = array(1);
}
- $grants['example_owner'] = array($account->uid);
+ $grants['example_author'] = array($account->uid);
return $grants;
}
@@ -264,6 +265,7 @@ function hook_node_grants($account, $op) {
* @return
* An array of grants as defined above.
*
+ * @see hook_node_access_records_alter()
* @ingroup node_access
*/
function hook_node_access_records($node) {
@@ -350,12 +352,11 @@ function hook_node_access_records_alter(&$grants, $node) {
* Alter user access rules when trying to view, edit or delete a node.
*
* Node access modules establish rules for user access to content.
- * hook_node_grants() defines permissions for a user to view, edit or
- * delete nodes by building a $grants array that indicates the permissions
- * assigned to the user by each node access module. This hook is called to allow
- * modules to modify the $grants array by reference, so the interaction of
- * multiple node access modules can be altered or advanced business logic can be
- * applied.
+ * hook_node_grants() defines permissions for a user to view, edit or delete
+ * nodes by building a $grants array that indicates the permissions assigned to
+ * the user by each node access module. This hook is called to allow modules to
+ * modify the $grants array by reference, so the interaction of multiple node
+ * access modules can be altered or advanced business logic can be applied.
*
* @see hook_node_grants()
*
@@ -374,8 +375,8 @@ function hook_node_access_records_alter(&$grants, $node) {
* @param $op
* The operation being performed, 'view', 'update' or 'delete'.
*
- * Developers may use this hook to either add additional grants to a user
- * or to remove existing grants. These rules are typically based on either the
+ * Developers may use this hook to either add additional grants to a user or to
+ * remove existing grants. These rules are typically based on either the
* permissions assigned to a user role, or specific attributes of a user
* account.
*
@@ -412,10 +413,10 @@ function hook_node_grants_alter(&$grants, $account, $op) {
* @return
* An array of operations. Each operation is an associative array that may
* contain the following key-value pairs:
- * - 'label': Required. The label for the operation, displayed in the dropdown
+ * - label: (required) The label for the operation, displayed in the dropdown
* menu.
- * - 'callback': Required. The function to call for the operation.
- * - 'callback arguments': Optional. An array of additional arguments to pass
+ * - callback: (required) The function to call for the operation.
+ * - callback arguments: (optional) An array of additional arguments to pass
* to the callback function.
*/
function hook_node_operations() {
@@ -528,11 +529,10 @@ function hook_node_insert($node) {
/**
* Act on arbitrary nodes being loaded from the database.
*
- * This hook should be used to add information that is not in the node or
- * node revisions table, not to replace information that is in these tables
- * (which could interfere with the entity cache). For performance reasons,
- * information for all available nodes should be loaded in a single query where
- * possible.
+ * This hook should be used to add information that is not in the node or node
+ * revisions table, not to replace information that is in these tables (which
+ * could interfere with the entity cache). For performance reasons, information
+ * for all available nodes should be loaded in a single query where possible.
*
* This hook is invoked during node loading, which is handled by entity_load(),
* via classes NodeController and DrupalDefaultEntityController. After the node
@@ -572,15 +572,15 @@ function hook_node_load($nodes, $types) {
* Modules may implement this hook if they want to have a say in whether or not
* a given user has access to perform a given operation on a node.
*
- * The administrative account (user ID #1) always passes any access check,
- * so this hook is not called in that case. Users with the "bypass node access"
+ * The administrative account (user ID #1) always passes any access check, so
+ * this hook is not called in that case. Users with the "bypass node access"
* permission may always view and edit content through the administrative
* interface.
*
- * Note that not all modules will want to influence access on all
- * node types. If your module does not want to actively grant or
- * block access, return NODE_ACCESS_IGNORE or simply return nothing.
- * Blindly returning FALSE will break other node access modules.
+ * Note that not all modules will want to influence access on all node types. If
+ * your module does not want to actively grant or block access, return
+ * NODE_ACCESS_IGNORE or simply return nothing. Blindly returning FALSE will
+ * break other node access modules.
*
* Also note that this function isn't called for node listings (e.g., RSS feeds,
* the default home page at path 'node', a recent content block, etc.) See
@@ -651,17 +651,17 @@ function hook_node_prepare($node) {
/**
* Act on a node being displayed as a search result.
*
- * This hook is invoked from node_search_execute(), after node_load()
- * and node_view() have been called.
+ * This hook is invoked from node_search_execute(), after node_load() and
+ * node_view() have been called.
*
* @param $node
* The node being displayed in a search result.
*
* @return array
* Extra information to be displayed with search result. This information
- * should be presented as an associative array. It will be concatenated
- * with the post information (last updated, author) in the default search
- * result theming.
+ * should be presented as an associative array. It will be concatenated with
+ * the post information (last updated, author) in the default search result
+ * theming.
*
* @see template_preprocess_search_result()
* @see search-result.tpl.php
@@ -724,8 +724,8 @@ function hook_node_update($node) {
/**
* Act on a node being indexed for searching.
*
- * This hook is invoked during search indexing, after node_load(), and after
- * the result of node_view() is added as $node->rendered to the node object.
+ * This hook is invoked during search indexing, after node_load(), and after the
+ * result of node_view() is added as $node->rendered to the node object.
*
* @param $node
* The node being indexed.
@@ -756,8 +756,8 @@ function hook_node_update_index($node) {
*
* Note: Changes made to the $node object within your hook implementation will
* have no effect. The preferred method to change a node's content is to use
- * hook_node_presave() instead. If it is really necessary to change
- * the node at the validate stage, you can use form_set_value().
+ * hook_node_presave() instead. If it is really necessary to change the node at
+ * the validate stage, you can use form_set_value().
*
* @param $node
* The node being validated.
@@ -874,8 +874,8 @@ function hook_node_view_alter(&$build) {
*
* This hook allows a module to define one or more of its own node types. For
* example, the blog module uses it to define a blog node-type named "Blog
- * entry." The name and attributes of each desired node type are specified in
- * an array returned by the hook.
+ * entry." The name and attributes of each desired node type are specified in an
+ * array returned by the hook.
*
* Only module-provided node types should be defined through this hook. User-
* provided (or 'custom') node types should be defined only in the 'node_type'
@@ -887,22 +887,21 @@ function hook_node_view_alter(&$build) {
* contains a sub-array for each node type, with the machine-readable type
* name as the key. Each sub-array has up to 10 attributes. Possible
* attributes:
- * - "name": the human-readable name of the node type. Required.
- * - "base": the base string used to construct callbacks corresponding to
- * this node type.
- * (i.e. if base is defined as example_foo, then example_foo_insert will
- * be called when inserting a node of that type). This string is usually
- * the name of the module, but not always. Required.
- * - "description": a brief description of the node type. Required.
- * - "help": help information shown to the user when creating a node of
- * this type.. Optional (defaults to '').
- * - "has_title": boolean indicating whether or not this node type has a title
- * field. Optional (defaults to TRUE).
- * - "title_label": the label for the title field of this content type.
- * Optional (defaults to 'Title').
- * - "locked": boolean indicating whether the administrator can change the
- * machine name of this type. FALSE = changeable (not locked),
- * TRUE = unchangeable (locked). Optional (defaults to TRUE).
+ * - name: (required) The human-readable name of the node type.
+ * - base: (required) The base name for implementations of node-type-specific
+ * hooks that respond to this node type. Base is usually the name of the
+ * module or 'node_content', but not always. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ * - description: (required) A brief description of the node type.
+ * - help: (optional) Help information shown to the user when creating a node
+ * of this type.
+ * - has_title: (optional) A Boolean indicating whether or not this node type
+ * has a title field.
+ * - title_label: (optional) The label for the title field of this content
+ * type.
+ * - locked: (optional) A Boolean indicating whether the administrator can
+ * change the machine name of this type. FALSE = changeable (not locked),
+ * TRUE = unchangeable (locked).
*
* The machine name of a node type should contain only letters, numbers, and
* underscores. Underscores will be converted into hyphens for the purpose of
@@ -950,20 +949,20 @@ function hook_node_info() {
* corresponding to the internal name of the ranking mechanism, such as
* 'recent', or 'comments'. The values should be arrays themselves, with the
* following keys available:
- * - "title": the human readable name of the ranking mechanism. Required.
- * - "join": part of a query string to join to any additional necessary
- * table. This is not necessary if the table required is already joined to
- * by the base query, such as for the {node} table. Other tables should use
- * the full table name as an alias to avoid naming collisions. Optional.
- * - "score": part of a query string to calculate the score for the ranking
- * mechanism based on values in the database. This does not need to be
- * wrapped in parentheses, as it will be done automatically; it also does
- * not need to take the weighted system into account, as it will be done
- * automatically. It does, however, need to calculate a decimal between
+ * - title: (required) The human readable name of the ranking mechanism.
+ * - join: (optional) The part of a query string to join to any additional
+ * necessary table. This is not necessary if the table required is already
+ * joined to by the base query, such as for the {node} table. Other tables
+ * should use the full table name as an alias to avoid naming collisions.
+ * - score: (required) The part of a query string to calculate the score for
+ * the ranking mechanism based on values in the database. This does not need
+ * to be wrapped in parentheses, as it will be done automatically; it also
+ * does not need to take the weighted system into account, as it will be
+ * done automatically. It does, however, need to calculate a decimal between
* 0 and 1; be careful not to cast the entire score to an integer by
- * inadvertently introducing a variable argument. Required.
- * - "arguments": if any arguments are required for the score, they can be
- * specified in an array here.
+ * inadvertently introducing a variable argument.
+ * - arguments: (optional) If any arguments are required for the score, they
+ * can be specified in an array here.
*
* @ingroup node_api_hooks
*/
@@ -990,8 +989,8 @@ function hook_ranking() {
/**
* Respond to node type creation.
*
- * This hook is invoked from node_type_save() after the node type is added
- * to the database.
+ * This hook is invoked from node_type_save() after the node type is added to
+ * the database.
*
* @param $info
* The node type object that is being created.
@@ -1003,8 +1002,8 @@ function hook_node_type_insert($info) {
/**
* Respond to node type updates.
*
- * This hook is invoked from node_type_save() after the node type is updated
- * in the database.
+ * This hook is invoked from node_type_save() after the node type is updated in
+ * the database.
*
* @param $info
* The node type object that is being updated.
@@ -1033,12 +1032,23 @@ function hook_node_type_delete($info) {
/**
* Respond to node deletion.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_delete() to respond to all node deletions).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
*
- * This hook is invoked from node_delete_multiple() after the node has been
- * removed from the node table in the database, before hook_node_delete() is
- * invoked, and before field_attach_delete() is called.
+ * Use hook_node_delete() to respond to node deletion of all node types.
+ *
+ * This hook is invoked from node_delete_multiple() before hook_node_delete()
+ * is invoked and before field_attach_delete() is called.
+ *
+ * Note that when this hook is invoked, the changes have not yet been written
+ * to the database, because a database transaction is still in progress. The
+ * transaction is not finalized until the delete operation is entirely
+ * completed and node_delete_multiple() goes out of scope. You should not rely
+ * on data in the database at this time as it is not updated yet. You should
+ * also note that any write/update database queries executed from this hook are
+ * also not committed immediately. Check node_delete_multiple() and
+ * db_transaction() for more info.
*
* @param $node
* The node that is being deleted.
@@ -1054,8 +1064,11 @@ function hook_delete($node) {
/**
* Act on a node object about to be shown on the add/edit form.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_prepare() to act on all node preparations).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_prepare() to respond to node preparation of all node types.
*
* This hook is invoked from node_object_prepare() before the general
* hook_node_prepare() is invoked.
@@ -1066,26 +1079,31 @@ function hook_delete($node) {
* @ingroup node_api_hooks
*/
function hook_prepare($node) {
- if ($file = file_check_upload($field_name)) {
- $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
- if ($file) {
- if (!image_get_info($file->uri)) {
- form_set_error($field_name, t('Uploaded file is not a valid image'));
- return;
- }
- }
- else {
+ $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
+ if ($file) {
+ if (!image_get_info($file->uri)) {
+ form_set_error($field_name, t('Uploaded file is not a valid image'));
return;
}
- $node->images['_original'] = $file->uri;
- _image_build_derivatives($node, TRUE);
- $node->new_file = TRUE;
}
+ else {
+ return;
+ }
+ $node->images['_original'] = $file->uri;
+ _image_build_derivatives($node, TRUE);
+ $node->new_file = TRUE;
}
/**
* Display a node editing form.
*
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_form_BASE_FORM_ID_alter(), with base form ID 'node_form', to alter
+ * node forms for all node types.
+ *
* This hook, implemented by node modules, is called to retrieve the form
* that is displayed to create or edit a node. This form is displayed at path
* node/add/[node type] or node/[node ID]/edit.
@@ -1141,8 +1159,11 @@ function hook_form($node, &$form_state) {
/**
* Respond to creation of a new node.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_insert() to act on all node insertions).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_insert() to respond to node insertion of all node types.
*
* This hook is invoked from node_save() after the node is inserted into the
* node table in the database, before field_attach_insert() is called, and
@@ -1165,8 +1186,11 @@ function hook_insert($node) {
/**
* Act on nodes being loaded from the database.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_load() to respond to all node loads).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_load() to respond to node load of all node types.
*
* This hook is invoked during node loading, which is handled by entity_load(),
* via classes NodeController and DrupalDefaultEntityController. After the node
@@ -1199,8 +1223,11 @@ function hook_load($nodes) {
/**
* Respond to updates to a node.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_update() to act on all node updates).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_update() to respond to node update of all node types.
*
* This hook is invoked from node_save() after the node is updated in the
* node table in the database, before field_attach_update() is called, and
@@ -1221,8 +1248,11 @@ function hook_update($node) {
/**
* Perform node validation before a node is created or updated.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_validate() to act on all node validations).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
+ *
+ * Use hook_node_validate() to respond to node validation of all node types.
*
* This hook is invoked from node_validate(), after a user has finished
* editing the node and is previewing or submitting it. It is invoked at the end
@@ -1255,32 +1285,38 @@ function hook_validate($node, $form, &$form_state) {
/**
* Display a node.
*
- * This hook is invoked only on the module that defines the node's content type
- * (use hook_node_view() to act on all node views).
+ * This is a node-type-specific hook, which is invoked only for the node type
+ * being affected. See
+ * @link node_api_hooks Node API hooks @endlink for more information.
*
- * This hook is invoked during node viewing after the node is fully loaded,
- * so that the node type module can define a custom method for display, or
- * add to the default display.
+ * Use hook_node_view() to respond to node view of all node types.
+ *
+ * This hook is invoked during node viewing after the node is fully loaded, so
+ * that the node type module can define a custom method for display, or add to
+ * the default display.
*
* @param $node
* The node to be displayed, as returned by node_load().
* @param $view_mode
* View mode, e.g. 'full', 'teaser', ...
+ * @param $langcode
+ * (optional) A language code to use for rendering. Defaults to the global
+ * content language of the current request.
+ *
* @return
- * $node. The passed $node parameter should be modified as necessary and
- * returned so it can be properly presented. Nodes are prepared for display
- * by assembling a structured array, formatted as in the Form API, in
- * $node->content. As with Form API arrays, the #weight property can be
- * used to control the relative positions of added elements. After this
- * hook is invoked, node_view() calls field_attach_view() to add field
- * views to $node->content, and then invokes hook_node_view() and
- * hook_node_view_alter(), so if you want to affect the final
- * view of the node, you might consider implementing one of these hooks
- * instead.
+ * The passed $node parameter should be modified as necessary and returned so
+ * it can be properly presented. Nodes are prepared for display by assembling
+ * a structured array, formatted as in the Form API, in $node->content. As
+ * with Form API arrays, the #weight property can be used to control the
+ * relative positions of added elements. After this hook is invoked,
+ * node_view() calls field_attach_view() to add field views to $node->content,
+ * and then invokes hook_node_view() and hook_node_view_alter(), so if you
+ * want to affect the final view of the node, you might consider implementing
+ * one of these hooks instead.
*
* @ingroup node_api_hooks
*/
-function hook_view($node, $view_mode) {
+function hook_view($node, $view_mode, $langcode = NULL) {
if ($view_mode == 'full' && node_is_page($node)) {
$breadcrumb = array();
$breadcrumb[] = l(t('Home'), NULL);
diff --git a/modules/node/node.info b/modules/node/node.info
index 85866e7..c91b28a 100644
--- a/modules/node/node.info
+++ b/modules/node/node.info
@@ -8,3 +8,9 @@ files[] = node.test
required = TRUE
configure = admin/structure/types
stylesheets[all][] = node.css
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/node/node.install b/modules/node/node.install
index 16d3dba..76c2aec 100644
--- a/modules/node/node.install
+++ b/modules/node/node.install
@@ -114,6 +114,7 @@ function node_schema() {
'uid' => array('uid'),
'tnid' => array('tnid'),
'translate' => array('translate'),
+ 'language' => array('language'),
),
'unique keys' => array(
'vid' => array('vid'),
@@ -914,6 +915,7 @@ function node_update_7012() {
* Change {node}.vid default value from 0 to NULL to avoid deadlock issues on MySQL.
*/
function node_update_7013() {
+ db_drop_unique_key('node', 'vid');
db_change_field('node', 'vid', 'vid', array(
'description' => 'The current {node_revision}.vid version identifier.',
'type' => 'int',
@@ -921,6 +923,14 @@ function node_update_7013() {
'not null' => FALSE,
'default' => NULL,
));
+ db_add_unique_key('node', 'vid', array('vid'));
+}
+
+/**
+ * Add an index on {node}.language.
+ */
+function node_update_7014() {
+ db_add_index('node', 'language', array('language'));
}
/**
diff --git a/modules/node/node.module b/modules/node/node.module
index d86c74d..fd848e2 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -141,6 +141,7 @@ function node_theme() {
),
'node_admin_overview' => array(
'variables' => array('name' => NULL, 'type' => NULL),
+ 'file' => 'content_types.inc',
),
'node_recent_block' => array(
'variables' => array('nodes' => NULL),
@@ -200,8 +201,8 @@ function node_entity_info() {
),
);
- // Search integration is provided by node.module, so search-related
- // view modes for nodes are defined here and not in search.module.
+ // Search integration is provided by node.module, so search-related view modes
+ // for nodes are defined here and not in search.module.
if (module_exists('search')) {
$return['node']['view modes'] += array(
'search_index' => array(
@@ -209,7 +210,7 @@ function node_entity_info() {
'custom settings' => FALSE,
),
'search_result' => array(
- 'label' => t('Search result'),
+ 'label' => t('Search result highlighting input'),
'custom settings' => FALSE,
),
);
@@ -243,7 +244,7 @@ function node_field_display_node_alter(&$display, $context) {
}
/**
- * Entity URI callback.
+ * Implements callback_entity_info_uri().
*/
function node_uri($node) {
return array(
@@ -296,7 +297,7 @@ function node_title_list($result, $title = NULL) {
}
/**
- * Update the 'last viewed' timestamp of the specified node for current user.
+ * Updates the 'last viewed' timestamp of the specified node for current user.
*
* @param $node
* A node object.
@@ -315,8 +316,14 @@ function node_tag_new($node) {
}
/**
- * Retrieves the timestamp at which the current user last viewed the
- * specified node.
+ * Retrieves the timestamp for the current user's last view of a specified node.
+ *
+ * @param $nid
+ * A node ID.
+ *
+ * @return
+ * If a node has been previously viewed by the user, the timestamp in seconds
+ * of when the last view occurred; otherwise, zero.
*/
function node_last_viewed($nid) {
global $user;
@@ -330,12 +337,13 @@ function node_last_viewed($nid) {
}
/**
- * Decide on the type of marker to be displayed for a given node.
+ * Determines the type of marker to be displayed for a given node.
*
* @param $nid
* Node ID whose history supplies the "last viewed" timestamp.
* @param $timestamp
* Time which is compared against node's "last viewed" timestamp.
+ *
* @return
* One of the MARK constants.
*/
@@ -359,7 +367,7 @@ function node_mark($nid, $timestamp) {
}
/**
- * Extract the type name.
+ * Extracts the type name.
*
* @param $node
* Either a string or object, containing the node type information.
@@ -461,6 +469,8 @@ function node_type_get_name($node) {
* node_type_save(), and obsolete ones are deleted via a call to
* node_type_delete(). See _node_types_build() for an explanation of the new
* and obsolete types.
+ *
+ * @see _node_types_build()
*/
function node_types_rebuild() {
_node_types_build(TRUE);
@@ -483,11 +493,35 @@ function node_type_load($name) {
/**
* Saves a node type to the database.
*
- * @param $info
- * The node type to save, as an object.
+ * @param object $info
+ * The node type to save; an object with the following properties:
+ * - type: A string giving the machine name of the node type.
+ * - name: A string giving the human-readable name of the node type.
+ * - base: A string that indicates the base string for hook functions. For
+ * example, 'node_content' is the value used by the UI when creating a new
+ * node type.
+ * - description: A string that describes the node type.
+ * - help: A string giving the help information shown to the user when
+ * creating a node of this type.
+ * - custom: TRUE or FALSE indicating whether this type is defined by a module
+ * (FALSE) or by a user (TRUE) via Add Content Type.
+ * - modified: TRUE or FALSE indicating whether this type has been modified by
+ * an administrator. When modifying an existing node type, set to TRUE, or
+ * the change will be ignored on node_types_rebuild().
+ * - locked: TRUE or FALSE indicating whether the administrator can change the
+ * machine name of this type.
+ * - disabled: TRUE or FALSE indicating whether this type has been disabled.
+ * - has_title: TRUE or FALSE indicating whether this type uses the node title
+ * field.
+ * - title_label: A string containing the label for the title.
+ * - module: A string giving the module defining this type of node.
+ * - orig_type: A string giving the original machine-readable name of this
+ * node type. This may be different from the current type name if the
+ * 'locked' key is FALSE.
*
- * @return
- * Status flag indicating outcome of the operation.
+ * @return int
+ * A status flag indicating the outcome of the operation, either SAVED_NEW or
+ * SAVED_UPDATED.
*/
function node_type_save($info) {
$existing_type = !empty($info->old_type) ? $info->old_type : $info->type;
@@ -540,7 +574,7 @@ function node_type_save($info) {
}
/**
- * Add default body field to a node type.
+ * Adds default body field to a node type.
*
* @param $type
* A node type object.
@@ -655,6 +689,7 @@ function node_type_update_nodes($old_type, $type) {
*
* @param $rebuild
* TRUE to rebuild node types. Equivalent to calling node_types_rebuild().
+ *
* @return
* An object with two properties:
* - names: Associative array of the names of node types, keyed by the type.
@@ -705,11 +740,9 @@ function _node_types_build($rebuild = FALSE) {
$type_db = $type_object->type;
// Original disabled value.
$disabled = $type_object->disabled;
- // Check for node types from disabled modules and mark their types for removal.
- // Types defined by the node module in the database (rather than by a separate
- // module using hook_node_info) have a base value of 'node_content'. The isset()
- // check prevents errors on old (pre-Drupal 7) databases.
- if (isset($type_object->base) && $type_object->base != 'node_content' && empty($_node_types->types[$type_db])) {
+ // Check for node types either from disabled modules or otherwise not defined
+ // and mark as disabled.
+ if (empty($type_object->custom) && empty($_node_types->types[$type_db])) {
$type_object->disabled = TRUE;
}
if (isset($_node_types->types[$type_db])) {
@@ -761,8 +794,9 @@ function node_type_cache_reset() {
* which prevents users from changing the machine name of the type.
*
* @param $info
- * An object or array containing values to override the defaults. See
- * hook_node_info() for details on what the array elements mean.
+ * (optional) An object or array containing values to override the defaults.
+ * See hook_node_info() for details on what the array elements mean. Defaults
+ * to an empty array.
*
* @return
* A node type object, with missing values in $info set to their defaults.
@@ -845,12 +879,13 @@ function node_rdf_mapping() {
}
/**
- * Determine whether a node hook exists.
+ * Determines whether a node hook exists.
*
* @param $node
* A node object or a string containing the node type.
* @param $hook
* A string containing the name of the hook.
+ *
* @return
* TRUE if the $hook exists in the node type of $node.
*/
@@ -860,7 +895,7 @@ function node_hook($node, $hook) {
}
/**
- * Invoke a node hook.
+ * Invokes a node hook.
*
* @param $node
* A node object or a string containing the node type.
@@ -868,6 +903,7 @@ function node_hook($node, $hook) {
* A string containing the name of the hook.
* @param $a2, $a3, $a4
* Arguments to pass on to the hook, after the $node argument.
+ *
* @return
* The returned value of the invoked hook.
*/
@@ -880,11 +916,11 @@ function node_invoke($node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
}
/**
- * Load node entities from the database.
+ * Loads node entities from the database.
*
* This function should be used whenever you need to load more than one node
- * from the database. Nodes are loaded into memory and will not require
- * database access if loaded again during the same page request.
+ * from the database. Nodes are loaded into memory and will not require database
+ * access if loaded again during the same page request.
*
* @see entity_load()
* @see EntityFieldQuery
@@ -910,7 +946,7 @@ function node_load_multiple($nids = array(), $conditions = array(), $reset = FAL
}
/**
- * Load a node object from the database.
+ * Loads a node object from the database.
*
* @param $nid
* The node ID.
@@ -934,6 +970,9 @@ function node_load($nid = NULL, $vid = NULL, $reset = FALSE) {
*
* Fills in a few default values, and then invokes hook_prepare() on the node
* type module, and hook_node_prepare() on all modules.
+ *
+ * @param $node
+ * A node object.
*/
function node_object_prepare($node) {
// Set up default values, if required.
@@ -963,11 +1002,11 @@ function node_object_prepare($node) {
}
/**
- * Perform validation checks on the given node.
+ * Implements hook_validate().
+ *
+ * Performs validation checks on the given node.
*/
function node_validate($node, $form, &$form_state) {
- $type = node_type_get_type($node);
-
if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) {
form_set_error('changed', t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.'));
}
@@ -1000,7 +1039,13 @@ function node_validate($node, $form, &$form_state) {
}
/**
- * Prepare node for saving by populating author and creation date.
+ * Prepares node for saving by populating author and creation date.
+ *
+ * @param $node
+ * A node object.
+ *
+ * @return
+ * An updated node object.
*/
function node_submit($node) {
// A user might assign the node author by entering a user name in the node
@@ -1021,7 +1066,7 @@ function node_submit($node) {
}
/**
- * Save changes to a node or add a new node.
+ * Saves changes to a node or adds a new node.
*
* @param $node
* The $node object to be saved. If $node->nid is
@@ -1133,10 +1178,8 @@ function node_save($node) {
module_invoke_all('node_' . $op, $node);
module_invoke_all('entity_' . $op, $node, 'node');
- // Update the node access table for this node. There's no need to delete
- // existing records if the node is new.
- $delete = $op == 'update';
- node_access_acquire_grants($node, $delete);
+ // Update the node access table for this node.
+ node_access_acquire_grants($node);
// Clear internal properties.
unset($node->is_new);
@@ -1159,6 +1202,13 @@ function node_save($node) {
* Helper function to save a revision with the uid of the current user.
*
* The resulting revision ID is available afterward in $node->vid.
+ *
+ * @param $node
+ * A node object.
+ * @param $uid
+ * The current user's UID.
+ * @param $update
+ * (optional) An array of primary keys' field names to update.
*/
function _node_save_revision($node, $uid, $update = NULL) {
$temp_uid = $node->uid;
@@ -1174,7 +1224,7 @@ function _node_save_revision($node, $uid, $update = NULL) {
}
/**
- * Delete a node.
+ * Deletes a node.
*
* @param $nid
* A node ID.
@@ -1184,7 +1234,7 @@ function node_delete($nid) {
}
/**
- * Delete multiple nodes.
+ * Deletes multiple nodes.
*
* @param $nids
* An array of node IDs.
@@ -1237,7 +1287,7 @@ function node_delete_multiple($nids) {
}
/**
- * Delete a node revision.
+ * Deletes a node revision.
*
* @param $revision_id
* The revision ID to delete.
@@ -1262,7 +1312,7 @@ function node_revision_delete($revision_id) {
}
/**
- * Generate an array for rendering the given node.
+ * Generates an array for rendering the given node.
*
* @param $node
* A node object.
@@ -1346,12 +1396,7 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
$node->content = array();
// Allow modules to change the view mode.
- $context = array(
- 'entity_type' => 'node',
- 'entity' => $node,
- 'langcode' => $langcode,
- );
- drupal_alter('entity_view_mode', $view_mode, $context);
+ $view_mode = key(entity_view_mode_prepare('node', array($node->nid => $node), $view_mode, $langcode));
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
@@ -1367,8 +1412,8 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
entity_prepare_view('node', array($node->nid => $node), $langcode);
$node->content += field_attach_view('node', $node, $view_mode, $langcode);
- // Always display a read more link on teasers because we have no way
- // to know when a teaser view is different than a full view.
+ // Always display a read more link on teasers because we have no way to know
+ // when a teaser view is different than a full view.
$links = array();
$node->content['links'] = array(
'#theme' => 'links__node',
@@ -1400,12 +1445,13 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) {
}
/**
- * Generate an array which displays a node detail page.
+ * Generates an array which displays a node detail page.
*
* @param $node
* A node object.
* @param $message
* A flag which sets a page title relevant to the revision being viewed.
+ *
* @return
* A $page element suitable for use by drupal_render().
*/
@@ -1428,6 +1474,9 @@ function node_show($node, $message = FALSE) {
*
* @param $node
* A node object.
+ *
+ * @return
+ * The ID of the node if this is a full page view, otherwise FALSE.
*/
function node_is_page($node) {
$page_node = menu_get_object();
@@ -1435,7 +1484,7 @@ function node_is_page($node) {
}
/**
- * Process variables for node.tpl.php
+ * Processes variables for node.tpl.php
*
* Most themes utilize their own copy of node.tpl.php. The default is located
* inside "modules/node/node.tpl.php". Look in there for the full list of
@@ -1530,6 +1579,7 @@ function node_permission() {
),
'access content overview' => array(
'title' => t('Access the content overview page'),
+ 'description' => t('Get an overview of all content.', array('@url' => url('admin/content'))),
),
'access content' => array(
'title' => t('View published content'),
@@ -1557,7 +1607,7 @@ function node_permission() {
}
/**
- * Gather the rankings from the the hook_ranking implementations.
+ * Gathers the rankings from the hook_ranking() implementations.
*
* @param $query
* A query object that has been extended with the Search DB Extender.
@@ -1625,7 +1675,7 @@ function node_search_admin() {
);
$form['content_ranking']['#theme'] = 'node_search_admin';
$form['content_ranking']['info'] = array(
- '#value' => '' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . ''
+ '#markup' => '' . t('Influence is a numeric multiplier used in ordering search results. A higher number means the corresponding factor has more influence on search results; zero means the factor is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '
'
);
// Note: reversed to reflect that higher number = higher ranking.
@@ -1804,6 +1854,7 @@ function node_user_delete($account) {
* An associative array containing:
* - form: A render element representing the form.
*
+ * @see node_search_admin()
* @ingroup themeable
*/
function theme_node_search_admin($variables) {
@@ -1811,7 +1862,7 @@ function theme_node_search_admin($variables) {
$output = drupal_render($form['info']);
- $header = array(t('Factor'), t('Weight'));
+ $header = array(t('Factor'), t('Influence'));
foreach (element_children($form['factors']) as $key) {
$row = array();
$row[] = $form['factors'][$key]['#title'];
@@ -1872,11 +1923,11 @@ function _node_revision_access($node, $op = 'view', $account = NULL) {
$node_current_revision = node_load($node->nid);
$is_current_revision = $node_current_revision->vid == $node->vid;
- // There should be at least two revisions. If the vid of the given node
- // and the vid of the current revision differ, then we already have two
+ // There should be at least two revisions. If the vid of the given node and
+ // the vid of the current revision differ, then we already have two
// different revisions so there is no need for a separate database check.
- // Also, if you try to revert to or delete the current revision, that's
- // not good.
+ // Also, if you try to revert to or delete the current revision, that's not
+ // good.
if ($is_current_revision && (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() == 1 || $op == 'update' || $op == 'delete')) {
$access[$cid] = FALSE;
}
@@ -1884,8 +1935,8 @@ function _node_revision_access($node, $op = 'view', $account = NULL) {
$access[$cid] = TRUE;
}
else {
- // First check the access to the current revision and finally, if the
- // node passed in is not the current revision then access to that, too.
+ // First check the access to the current revision and finally, if the node
+ // passed in is not the current revision then access to that, too.
$access[$cid] = node_access($op, $node_current_revision, $account) && ($is_current_revision || node_access($op, $node, $account));
}
}
@@ -1893,6 +1944,14 @@ function _node_revision_access($node, $op = 'view', $account = NULL) {
return $access[$cid];
}
+/**
+ * Access callback: Checks whether the user has permission to add a node.
+ *
+ * @return
+ * TRUE if the user has add permission, otherwise FALSE.
+ *
+ * @see node_menu()
+ */
function _node_add_access() {
$types = node_type_get_types();
foreach ($types as $type) {
@@ -2110,14 +2169,30 @@ function node_menu_local_tasks_alter(&$data, $router_item, $root_path) {
}
/**
- * Title callback for a node type.
+ * Title callback: Returns the unsanitized title of the node type edit form.
+ *
+ * @param $type
+ * The node type object.
+ *
+ * @return string
+ * An unsanitized string that is the title of the node type edit form.
+ *
+ * @see node_menu()
*/
function node_type_page_title($type) {
return $type->name;
}
/**
- * Title callback.
+ * Title callback: Returns the title of the node.
+ *
+ * @param $node
+ * The node object.
+ *
+ * @return
+ * An unsanitized string that is the title of the node.
+ *
+ * @see node_menu()
*/
function node_page_title($node) {
return $node->title;
@@ -2137,7 +2212,13 @@ function node_last_changed($nid) {
}
/**
- * Return a list of all the existing revision numbers.
+ * Returns a list of all the existing revision numbers.
+ *
+ * @param $node
+ * The node object.
+ *
+ * @return
+ * An associative array keyed by node revision number.
*/
function node_revision_list($node) {
$revisions = array();
@@ -2223,16 +2304,16 @@ function node_block_save($delta = '', $edit = array()) {
* (optional) The maximum number of nodes to find. Defaults to 10.
*
* @return
- * An array of partial node objects or an empty array if there are no recent
- * nodes visible to the current user.
+ * An array of node entities or an empty array if there are no recent nodes
+ * visible to the current user.
*/
function node_get_recent($number = 10) {
$query = db_select('node', 'n');
if (!user_access('bypass node access')) {
- // If the user is able to view their own unpublished nodes, allow them
- // to see these in addition to published nodes. Check that they actually
- // have some unpublished nodes to view before adding the condition.
+ // If the user is able to view their own unpublished nodes, allow them to
+ // see these in addition to published nodes. Check that they actually have
+ // some unpublished nodes to view before adding the condition.
if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->uid, ':status' => NODE_NOT_PUBLISHED))->fetchCol()) {
$query->condition(db_or()
->condition('n.status', NODE_PUBLISHED)
@@ -2362,7 +2443,7 @@ function node_form_block_admin_configure_alter(&$form, &$form_state) {
}
/**
- * Form submit handler for block configuration form.
+ * Form submission handler for node_form_block_admin_configure_alter().
*
* @see node_form_block_admin_configure_alter()
*/
@@ -2394,7 +2475,7 @@ function node_form_block_custom_block_delete_alter(&$form, &$form_state) {
}
/**
- * Form submit handler for custom block delete form.
+ * Form submission handler for node_form_block_custom_block_delete_alter().
*
* @see node_form_block_custom_block_delete_alter()
*/
@@ -2419,8 +2500,8 @@ function node_modules_uninstalled($modules) {
/**
* Implements hook_block_list_alter().
*
- * Check the content type specific visibilty settings.
- * Remove the block if the visibility conditions are not met.
+ * Check the content type specific visibilty settings. Remove the block if the
+ * visibility conditions are not met.
*/
function node_block_list_alter(&$blocks) {
global $theme_key;
@@ -2485,7 +2566,8 @@ function node_block_list_alter(&$blocks) {
* @param $channel
* An associative array containing title, link, description and other keys,
* to be parsed by format_rss_channel() and format_xml_elements().
- * A list of channel elements can be found at the @link http://cyber.law.harvard.edu/rss/rss.html RSS 2.0 Specification. @endlink
+ * A list of channel elements can be found at the
+ * @link http://cyber.law.harvard.edu/rss/rss.html RSS 2.0 Specification. @endlink
* The link should be an absolute URL.
*/
function node_feed($nids = FALSE, $channel = array()) {
@@ -2505,7 +2587,6 @@ function node_feed($nids = FALSE, $channel = array()) {
$item_length = variable_get('feed_item_length', 'fulltext');
$namespaces = array('xmlns:dc' => 'http://purl.org/dc/elements/1.1/');
- $teaser = ($item_length == 'teaser');
// Load all nodes to be rendered.
$nodes = node_load_multiple($nids);
@@ -2515,9 +2596,10 @@ function node_feed($nids = FALSE, $channel = array()) {
$node->link = url("node/$node->nid", array('absolute' => TRUE));
$node->rss_namespaces = array();
+ $account = user_load($node->uid);
$node->rss_elements = array(
array('key' => 'pubDate', 'value' => gmdate('r', $node->created)),
- array('key' => 'dc:creator', 'value' => $node->name),
+ array('key' => 'dc:creator', 'value' => format_username($account)),
array('key' => 'guid', 'value' => $node->nid . ' at ' . $base_url, 'attributes' => array('isPermaLink' => 'false'))
);
@@ -2559,7 +2641,7 @@ function node_feed($nids = FALSE, $channel = array()) {
}
/**
- * Construct a drupal_render() style array from an array of loaded nodes.
+ * Constructs a drupal_render() style array from an array of loaded nodes.
*
* @param $nodes
* An array of nodes as returned by node_load_multiple().
@@ -2568,27 +2650,43 @@ function node_feed($nids = FALSE, $channel = array()) {
* @param $weight
* An integer representing the weight of the first node in the list.
* @param $langcode
- * (optional) A language code to use for rendering. Defaults to the global
- * content language of the current request.
+ * (optional) A language code to use for rendering. Defaults to NULL which is
+ * the global content language of the current request.
*
* @return
* An array in the format expected by drupal_render().
*/
function node_view_multiple($nodes, $view_mode = 'teaser', $weight = 0, $langcode = NULL) {
- field_attach_prepare_view('node', $nodes, $view_mode, $langcode);
- entity_prepare_view('node', $nodes, $langcode);
$build = array();
+ $entities_by_view_mode = entity_view_mode_prepare('node', $nodes, $view_mode, $langcode);
+ foreach ($entities_by_view_mode as $entity_view_mode => $entities) {
+ field_attach_prepare_view('node', $entities, $entity_view_mode, $langcode);
+ entity_prepare_view('node', $entities, $langcode);
+
+ foreach ($entities as $entity) {
+ $build['nodes'][$entity->nid] = node_view($entity, $entity_view_mode, $langcode);
+ }
+ }
+
foreach ($nodes as $node) {
- $build['nodes'][$node->nid] = node_view($node, $view_mode, $langcode);
$build['nodes'][$node->nid]['#weight'] = $weight;
$weight++;
}
+ // Sort here, to preserve the input order of the entities that were passed to
+ // this function.
+ uasort($build['nodes'], 'element_sort');
$build['nodes']['#sorted'] = TRUE;
+
return $build;
}
/**
- * Menu callback; Generate a listing of promoted nodes.
+ * Menu callback: Generates a listing of promoted nodes.
+ *
+ * @return array
+ * An array in the format expected by drupal_render().
+ *
+ * @see node_menu()
*/
function node_page_default() {
$select = db_select('node', 'n')
@@ -2638,7 +2736,15 @@ function node_page_default() {
}
/**
- * Menu callback; view a single node.
+ * Menu callback: Displays a single node.
+ *
+ * @param $node
+ * The node object.
+ *
+ * @return
+ * A page array suitable for use by drupal_render().
+ *
+ * @see node_menu()
*/
function node_page_view($node) {
// If there is a menu link to this node, the link becomes the last part
@@ -2667,7 +2773,7 @@ function node_update_index() {
}
/**
- * Index a single node.
+ * Indexes a single node.
*
* @param $node
* The node to index.
@@ -2771,7 +2877,7 @@ function node_form_search_form_alter(&$form, $form_state) {
}
/**
- * Form API callback for the search form. Registered in node_form_alter().
+ * Form validation handler for node_form_alter().
*/
function node_search_validate($form, &$form_state) {
// Initialize using any existing basic search keywords.
@@ -2819,8 +2925,8 @@ function node_search_validate($form, &$form_state) {
* @{
* The node access system determines who can do what to which nodes.
*
- * In determining access rights for a node, node_access() first checks
- * whether the user has the "bypass node access" permission. Such users have
+ * In determining access rights for a node, node_access() first checks whether
+ * the user has the "bypass node access" permission. Such users have
* unrestricted access to all nodes. user 1 will always pass this check.
*
* Next, all implementations of hook_node_access() will be called. Each
@@ -2858,8 +2964,7 @@ function node_search_validate($form, &$form_state) {
*/
/**
- * Determine whether the current user may perform the given operation on the
- * specified node.
+ * Determines whether the current user may perform the operation on the node.
*
* @param $op
* The operation to be performed on the node. Possible values are:
@@ -2873,6 +2978,7 @@ function node_search_validate($form, &$form_state) {
* @param $account
* Optional, a user object representing the user for whom the operation is to
* be performed. Determines access for a user other than the current user.
+ *
* @return
* TRUE if the operation may be performed, FALSE otherwise.
*/
@@ -3005,6 +3111,7 @@ function node_node_access($node, $op, $account) {
*
* @param $type
* The machine-readable name of the node type.
+ *
* @return array
* An array of permission names and descriptions.
*/
@@ -3038,11 +3145,11 @@ function node_list_permissions($type) {
*
* By default, this will include all node types in the system. To exclude a
* specific node from getting permissions defined for it, set the
- * node_permissions_$type variable to 0. Core does not provide an interface
- * for doing so, however, contrib modules may exclude their own nodes in
+ * node_permissions_$type variable to 0. Core does not provide an interface for
+ * doing so. However, contrib modules may exclude their own nodes in
* hook_install(). Alternatively, contrib modules may configure all node types
- * at once, or decide to apply some other hook_node_access() implementation
- * to some or all node types.
+ * at once, or decide to apply some other hook_node_access() implementation to
+ * some or all node types.
*
* @return
* An array of node types managed by this module.
@@ -3061,21 +3168,22 @@ function node_permissions_get_configured_types() {
}
/**
- * Fetch an array of permission IDs granted to the given user ID.
+ * Fetches an array of permission IDs granted to the given user ID.
*
* The implementation here provides only the universal "all" grant. A node
- * access module should implement hook_node_grants() to provide a grant
- * list for the user.
+ * access module should implement hook_node_grants() to provide a grant list for
+ * the user.
*
- * After the default grants have been loaded, we allow modules to alter
- * the grants array by reference. This hook allows for complex business
- * logic to be applied when integrating multiple node access modules.
+ * After the default grants have been loaded, we allow modules to alter the
+ * grants array by reference. This hook allows for complex business logic to be
+ * applied when integrating multiple node access modules.
*
* @param $op
* The operation that the user is trying to perform.
* @param $account
* The user object for the user performing the operation. If omitted, the
* current user is used.
+ *
* @return
* An associative array in which the keys are realms, and the values are
* arrays of grants for those realms.
@@ -3163,11 +3271,10 @@ function node_access_view_all_nodes($account = NULL) {
/**
* Implements hook_query_TAG_alter().
*
- * This is the hook_query_alter() for queries tagged with 'node_access'.
- * It adds node access checks for the user account given by the 'account'
- * meta-data (or global $user if not provided), for an operation given by
- * the 'op' meta-data (or 'view' if not provided; other possible values are
- * 'update' and 'delete').
+ * This is the hook_query_alter() for queries tagged with 'node_access'. It adds
+ * node access checks for the user account given by the 'account' meta-data (or
+ * global $user if not provided), for an operation given by the 'op' meta-data
+ * (or 'view' if not provided; other possible values are 'update' and 'delete').
*/
function node_query_node_access_alter(QueryAlterableInterface $query) {
_node_query_node_access_alter($query, 'node');
@@ -3187,6 +3294,17 @@ function node_query_entity_field_access_alter(QueryAlterableInterface $query) {
/**
* Helper for node access functions.
*
+ * Queries tagged with 'node_access' that are not against the {node} table
+ * should add the base table as metadata. For example:
+ * @code
+ * $query
+ * ->addTag('node_access')
+ * ->addMetaData('base_table', 'taxonomy_index');
+ * @endcode
+ * If the query is not against the {node} table, an attempt is made to guess
+ * the table, but is not recommended to rely on this as it is deprecated and not
+ * allowed in Drupal 8. It is always safer to provide the table.
+ *
* @param $query
* The query to add conditions to.
* @param $type
@@ -3206,8 +3324,8 @@ function _node_query_node_access_alter($query, $type) {
}
// If $account can bypass node access, or there are no node access modules,
- // or the operation is 'view' and the $acount has a global view grant (i.e.,
- // a view grant for node ID 0), we don't need to alter the query.
+ // or the operation is 'view' and the $account has a global view grant
+ // (such as a view grant for node ID 0), we don't need to alter the query.
if (user_access('bypass node access', $account)) {
return;
}
@@ -3394,15 +3512,14 @@ function node_access_acquire_grants($node, $delete = TRUE) {
*
* If a realm is provided, it will only delete grants from that realm, but it
* will always delete a grant from the 'all' realm. Modules that utilize
- * node_access can use this function when doing mass updates due to widespread
+ * node_access() can use this function when doing mass updates due to widespread
* permission changes.
*
* Note: Don't call this function directly from a contributed module. Call
* node_access_acquire_grants() instead.
*
* @param $node
- * The $node being written to. All that is necessary is that it contains a
- * nid.
+ * The node whose grants are being written.
* @param $grants
* A list of grants to write. Each grant is an array that must contain the
* following keys: realm, gid, grant_view, grant_update, grant_delete.
@@ -3410,10 +3527,14 @@ function node_access_acquire_grants($node, $delete = TRUE) {
* is a module-defined id to define grant privileges. each grant_* field
* is a boolean value.
* @param $realm
- * If provided, only read/write grants for that realm.
+ * (optional) If provided, read/write grants for that realm only. Defaults to
+ * NULL.
* @param $delete
- * If false, do not delete records. This is only for optimization purposes,
- * and assumes the caller has already performed a mass delete of some form.
+ * (optional) If false, does not delete records. This is only for optimization
+ * purposes, and assumes the caller has already performed a mass delete of
+ * some form. Defaults to TRUE.
+ *
+ * @see node_access_acquire_grants()
*/
function node_access_write_grants($node, $grants, $realm = NULL, $delete = TRUE) {
if ($delete) {
@@ -3442,21 +3563,23 @@ function node_access_write_grants($node, $grants, $realm = NULL, $delete = TRUE)
}
/**
- * Flag / unflag the node access grants for rebuilding, or read the current
- * value of the flag.
+ * Flags or unflags the node access grants for rebuilding.
*
+ * If the argument isn't specified, the current value of the flag is returned.
* When the flag is set, a message is displayed to users with 'access
* administration pages' permission, pointing to the 'rebuild' confirm form.
* This can be used as an alternative to direct node_access_rebuild calls,
* allowing administrators to decide when they want to perform the actual
- * (possibly time consuming) rebuild.
- * When unsure the current user is an administrator, node_access_rebuild
- * should be used instead.
+ * (possibly time consuming) rebuild. When unsure if the current user is an
+ * administrator, node_access_rebuild() should be used instead.
*
* @param $rebuild
* (Optional) The boolean value to be written.
- * @return
- * (If no value was provided for $rebuild) The current value of the flag.
+ *
+ * @return
+ * The current value of the flag if no value was provided for $rebuild.
+ *
+ * @see node_access_rebuild()
*/
function node_access_needs_rebuild($rebuild = NULL) {
if (!isset($rebuild)) {
@@ -3471,15 +3594,15 @@ function node_access_needs_rebuild($rebuild = NULL) {
}
/**
- * Rebuild the node access database. This is occasionally needed by modules
- * that make system-wide changes to access levels.
+ * Rebuilds the node access database.
*
- * When the rebuild is required by an admin-triggered action (e.g module
- * settings form), calling node_access_needs_rebuild(TRUE) instead of
+ * This is occasionally needed by modules that make system-wide changes to
+ * access levels. When the rebuild is required by an admin-triggered action (e.g
+ * module settings form), calling node_access_needs_rebuild(TRUE) instead of
* node_access_rebuild() lets the user perform his changes and actually
* rebuild only once he is done.
*
- * Note : As of Drupal 6, node access modules are not required to (and actually
+ * Note: As of Drupal 6, node access modules are not required to (and actually
* should not) call node_access_rebuild() in hook_enable/disable anymore.
*
* @see node_access_needs_rebuild()
@@ -3510,7 +3633,8 @@ function node_access_rebuild($batch_mode = FALSE) {
// Try to allocate enough time to rebuild node grants
drupal_set_time_limit(240);
- $nids = db_query("SELECT nid FROM {node}")->fetchCol();
+ // Rebuild newest nodes first so that recent content becomes available quickly.
+ $nids = db_query("SELECT nid FROM {node} ORDER BY nid DESC")->fetchCol();
foreach ($nids as $nid) {
$node = node_load($nid, NULL, TRUE);
// To preserve database integrity, only acquire grants if the node
@@ -3543,11 +3667,14 @@ function node_access_rebuild($batch_mode = FALSE) {
}
/**
- * Batch operation for node_access_rebuild_batch.
+ * Performs batch operation for node_access_rebuild().
*
- * This is a multistep operation : we go through all nodes by packs of 20.
- * The batch processing engine interrupts processing and sends progress
- * feedback after 1 second execution time.
+ * This is a multistep operation: we go through all nodes by packs of 20. The
+ * batch processing engine interrupts processing and sends progress feedback
+ * after 1 second execution time.
+ *
+ * @param array $context
+ * An array of contextual key/value information for rebuild batch process.
*/
function _node_access_rebuild_batch_operation(&$context) {
if (empty($context['sandbox'])) {
@@ -3578,7 +3705,14 @@ function _node_access_rebuild_batch_operation(&$context) {
}
/**
- * Post-processing for node_access_rebuild_batch.
+ * Performs post-processing for node_access_rebuild().
+ *
+ * @param bool $success
+ * A boolean indicating whether the re-build process has completed.
+ * @param array $results
+ * An array of results information.
+ * @param array $operations
+ * An array of function calls (not used in this function).
*/
function _node_access_rebuild_batch_finished($success, $results, $operations) {
if ($success) {
@@ -3595,7 +3729,6 @@ function _node_access_rebuild_batch_finished($success, $results, $operations) {
* @} End of "defgroup node_access".
*/
-
/**
* @defgroup node_content Hook implementations for user-created content types
* @{
@@ -3631,6 +3764,7 @@ function node_content_form($node, $form_state) {
/**
* Implements hook_forms().
+ *
* All node forms share the same form handler.
*/
function node_forms() {
@@ -3715,6 +3849,12 @@ function node_action_info() {
/**
* Sets the status of a node to 1 (published).
*
+ * @param $node
+ * A node object.
+ * @param $context
+ * (optional) Array of additional information about what triggered the action.
+ * Not used for this action.
+ *
* @ingroup actions
*/
function node_publish_action($node, $context = array()) {
@@ -3725,6 +3865,12 @@ function node_publish_action($node, $context = array()) {
/**
* Sets the status of a node to 0 (unpublished).
*
+ * @param $node
+ * A node object.
+ * @param $context
+ * (optional) Array of additional information about what triggered the action.
+ * Not used for this action.
+ *
* @ingroup actions
*/
function node_unpublish_action($node, $context = array()) {
@@ -3735,6 +3881,12 @@ function node_unpublish_action($node, $context = array()) {
/**
* Sets the sticky-at-top-of-list property of a node to 1.
*
+ * @param $node
+ * A node object.
+ * @param $context
+ * (optional) Array of additional information about what triggered the action.
+ * Not used for this action.
+ *
* @ingroup actions
*/
function node_make_sticky_action($node, $context = array()) {
@@ -3745,6 +3897,12 @@ function node_make_sticky_action($node, $context = array()) {
/**
* Sets the sticky-at-top-of-list property of a node to 0.
*
+ * @param $node
+ * A node object.
+ * @param $context
+ * (optional) Array of additional information about what triggered the action.
+ * Not used for this action.
+ *
* @ingroup actions
*/
function node_make_unsticky_action($node, $context = array()) {
@@ -3755,6 +3913,12 @@ function node_make_unsticky_action($node, $context = array()) {
/**
* Sets the promote property of a node to 1.
*
+ * @param $node
+ * A node object.
+ * @param $context
+ * (optional) Array of additional information about what triggered the action.
+ * Not used for this action.
+ *
* @ingroup actions
*/
function node_promote_action($node, $context = array()) {
@@ -3765,6 +3929,12 @@ function node_promote_action($node, $context = array()) {
/**
* Sets the promote property of a node to 0.
*
+ * @param $node
+ * A node object.
+ * @param $context
+ * (optional) Array of additional information about what triggered the action.
+ * Not used for this action.
+ *
* @ingroup actions
*/
function node_unpromote_action($node, $context = array()) {
@@ -3775,6 +3945,9 @@ function node_unpromote_action($node, $context = array()) {
/**
* Saves a node.
*
+ * @param $node
+ * The node to be saved.
+ *
* @ingroup actions
*/
function node_save_action($node) {
@@ -3791,6 +3964,9 @@ function node_save_action($node) {
* Array with the following elements:
* - 'owner_uid': User ID to assign to the node.
*
+ * @see node_assign_owner_action_form()
+ * @see node_assign_owner_action_validate()
+ * @see node_assign_owner_action_submit()
* @ingroup actions
*/
function node_assign_owner_action($node, $context) {
@@ -3801,6 +3977,16 @@ function node_assign_owner_action($node, $context) {
/**
* Generates the settings form for node_assign_owner_action().
+ *
+ * @param $context
+ * Array of additional information about what triggered the action. Includes
+ * the following elements:
+ * - 'owner_uid': User ID to assign to the node.
+ *
+ * @see node_assign_owner_action_submit()
+ * @see node_assign_owner_action_validate()
+ *
+ * @ingroup forms
*/
function node_assign_owner_action_form($context) {
$description = t('The username of the user to which you would like to assign ownership.');
@@ -3841,6 +4027,8 @@ function node_assign_owner_action_form($context) {
/**
* Validates settings form for node_assign_owner_action().
+ *
+ * @see node_assign_owner_action_submit()
*/
function node_assign_owner_action_validate($form, $form_state) {
$exists = (bool) db_query_range('SELECT 1 FROM {users} WHERE name = :name', 0, 1, array(':name' => $form_state['values']['owner_name']))->fetchField();
@@ -3851,6 +4039,8 @@ function node_assign_owner_action_validate($form, $form_state) {
/**
* Saves settings form for node_assign_owner_action().
+ *
+ * @see node_assign_owner_action_validate()
*/
function node_assign_owner_action_submit($form, $form_state) {
// Username can change, so we need to store the ID, not the username.
@@ -3860,6 +4050,14 @@ function node_assign_owner_action_submit($form, $form_state) {
/**
* Generates settings form for node_unpublish_by_keyword_action().
+ *
+ * @param array $context
+ * Array of additional information about what triggered this action.
+ *
+ * @return array
+ * A form array.
+ *
+ * @see node_unpublish_by_keyword_action_submit()
*/
function node_unpublish_by_keyword_action_form($context) {
$form['keywords'] = array(
diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc
index c6cb1bc..cc3908e 100644
--- a/modules/node/node.pages.inc
+++ b/modules/node/node.pages.inc
@@ -5,7 +5,6 @@
* Page callbacks for adding, editing, deleting, and revisions management for content.
*/
-
/**
* Menu callback; presents the node editing form.
*/
@@ -63,6 +62,12 @@ function theme_node_add_list($variables) {
/**
* Returns a node submission form.
+ *
+ * @param $type
+ * The node type for the submitted node.
+ *
+ * @return
+ * The themed form.
*/
function node_add($type) {
global $user;
@@ -75,6 +80,12 @@ function node_add($type) {
return $output;
}
+/**
+ * Form validation handler for node_form().
+ *
+ * @see node_form()
+ * @see node_form_submit()
+ */
function node_form_validate($form, &$form_state) {
// $form_state['node'] contains the actual entity being edited, but we must
// not update it with form values that have not yet been validated, so we
@@ -85,7 +96,13 @@ function node_form_validate($form, &$form_state) {
}
/**
- * Generate the node add/edit form array.
+ * Form constructor for the node add/edit form.
+ *
+ * @see node_form_validate()
+ * @see node_form_submit()
+ * @see node_form_build_preview()
+ * @see node_form_delete_submit()
+ * @ingroup forms
*/
function node_form($form, &$form_state, $node) {
global $user;
@@ -311,7 +328,12 @@ function node_form($form, &$form_state, $node) {
}
/**
- * Button submit function: handle the 'Delete' button on the node form.
+ * Form submission handler for node_form().
+ *
+ * Handles the 'Delete' button on the node form.
+ *
+ * @see node_form()
+ * @see node_form_validate()
*/
function node_form_delete_submit($form, &$form_state) {
$destination = array();
@@ -323,7 +345,14 @@ function node_form_delete_submit($form, &$form_state) {
$form_state['redirect'] = array('node/' . $node->nid . '/delete', array('query' => $destination));
}
-
+/**
+ * Form submission handler for node_form().
+ *
+ * Handles the 'Preview' button on the node form.
+ *
+ * @see node_form()
+ * @see node_form_validate()
+ */
function node_form_build_preview($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state);
$form_state['node_preview'] = node_preview($node);
@@ -331,38 +360,49 @@ function node_form_build_preview($form, &$form_state) {
}
/**
- * Generate a node preview.
+ * Generates a node preview.
+ *
+ * @param $node
+ * The node to preview.
+ *
+ * @return
+ * An HTML-formatted string of a node preview.
+ *
+ * @see node_form_build_preview()
*/
function node_preview($node) {
- if (node_access('create', $node) || node_access('update', $node)) {
- _field_invoke_multiple('load', 'node', array($node->nid => $node));
+ // Clone the node before previewing it to prevent the node itself from being
+ // modified.
+ $cloned_node = clone $node;
+ if (node_access('create', $cloned_node) || node_access('update', $cloned_node)) {
+ _field_invoke_multiple('load', 'node', array($cloned_node->nid => $cloned_node));
// Load the user's name when needed.
- if (isset($node->name)) {
+ if (isset($cloned_node->name)) {
// The use of isset() is mandatory in the context of user IDs, because
// user ID 0 denotes the anonymous user.
- if ($user = user_load_by_name($node->name)) {
- $node->uid = $user->uid;
- $node->picture = $user->picture;
+ if ($user = user_load_by_name($cloned_node->name)) {
+ $cloned_node->uid = $user->uid;
+ $cloned_node->picture = $user->picture;
}
else {
- $node->uid = 0; // anonymous user
+ $cloned_node->uid = 0; // anonymous user
}
}
- elseif ($node->uid) {
- $user = user_load($node->uid);
- $node->name = $user->name;
- $node->picture = $user->picture;
+ elseif ($cloned_node->uid) {
+ $user = user_load($cloned_node->uid);
+ $cloned_node->name = $user->name;
+ $cloned_node->picture = $user->picture;
}
- $node->changed = REQUEST_TIME;
- $nodes = array($node->nid => $node);
+ $cloned_node->changed = REQUEST_TIME;
+ $nodes = array($cloned_node->nid => $cloned_node);
field_attach_prepare_view('node', $nodes, 'full');
// Display a preview of the node.
if (!form_get_errors()) {
- $node->in_preview = TRUE;
- $output = theme('node_preview', array('node' => $node));
- unset($node->in_preview);
+ $cloned_node->in_preview = TRUE;
+ $output = theme('node_preview', array('node' => $cloned_node));
+ unset($cloned_node->in_preview);
}
drupal_set_title(t('Preview'), PASS_THROUGH);
@@ -377,6 +417,7 @@ function node_preview($node) {
* An associative array containing:
* - node: The node object which is being previewed.
*
+ * @see node_preview()
* @ingroup themeable
*/
function theme_node_preview($variables) {
@@ -407,6 +448,12 @@ function theme_node_preview($variables) {
return $output;
}
+/**
+ * Form submission handler for node_form().
+ *
+ * @see node_form()
+ * @see node_form_validate()
+ */
function node_form_submit($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state);
$insert = empty($node->nid);
@@ -426,7 +473,7 @@ function node_form_submit($form, &$form_state) {
if ($node->nid) {
$form_state['values']['nid'] = $node->nid;
$form_state['nid'] = $node->nid;
- $form_state['redirect'] = 'node/' . $node->nid;
+ $form_state['redirect'] = node_access('view', $node) ? 'node/' . $node->nid : '';
}
else {
// In the unlikely case something went wrong on save, the node will be
@@ -472,7 +519,9 @@ function node_form_submit_build_node($form, &$form_state) {
}
/**
- * Menu callback -- ask for confirmation of node deletion
+ * Form constructor for the node deletion confirmation form.
+ *
+ * @see node_delete_confirm_submit()
*/
function node_delete_confirm($form, &$form_state, $node) {
$form['#node'] = $node;
@@ -488,12 +537,15 @@ function node_delete_confirm($form, &$form_state, $node) {
}
/**
- * Execute node deletion
+ * Executes node deletion.
+ *
+ * @see node_delete_confirm()
*/
function node_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
$node = node_load($form_state['values']['nid']);
node_delete($form_state['values']['nid']);
+ cache_clear_all();
watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->title));
drupal_set_message(t('@type %title has been deleted.', array('@type' => node_type_get_name($node), '%title' => $node->title)));
}
@@ -502,7 +554,15 @@ function node_delete_confirm_submit($form, &$form_state) {
}
/**
- * Generate an overview table of older revisions of a node.
+ * Generates an overview table of older revisions of a node.
+ *
+ * @param $node
+ * A node object.
+ *
+ * @return array
+ * An array as expected by drupal_render().
+ *
+ * @see node_menu()
*/
function node_revision_overview($node) {
drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH);
@@ -553,13 +613,26 @@ function node_revision_overview($node) {
}
/**
- * Ask for confirmation of the reversion to prevent against CSRF attacks.
+ * Asks for confirmation of the reversion to prevent against CSRF attacks.
+ *
+ * @param int $node_revision
+ * The node revision ID.
+ *
+ * @return array
+ * An array as expected by drupal_render().
+ *
+ * @see node_menu()
+ * @see node_revision_revert_confirm_submit()
+ * @ingroup forms
*/
function node_revision_revert_confirm($form, $form_state, $node_revision) {
$form['#node_revision'] = $node_revision;
return confirm_form($form, t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', '', t('Revert'), t('Cancel'));
}
+/**
+ * Form submission handler for node_revision_revert_confirm().
+ */
function node_revision_revert_confirm_submit($form, &$form_state) {
$node_revision = $form['#node_revision'];
$node_revision->revision = 1;
@@ -572,11 +645,29 @@ function node_revision_revert_confirm_submit($form, &$form_state) {
$form_state['redirect'] = 'node/' . $node_revision->nid . '/revisions';
}
+/**
+ * Form constructor for the revision deletion confirmation form.
+ *
+ * This form prevents against CSRF attacks.
+ *
+ * @param $node_revision
+ * The node revision ID.
+ *
+ * @return
+ * An array as expected by drupal_render().
+ *
+ * @see node_menu()
+ * @see node_revision_delete_confirm_submit()
+ * @ingroup forms
+ */
function node_revision_delete_confirm($form, $form_state, $node_revision) {
$form['#node_revision'] = $node_revision;
return confirm_form($form, t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
+/**
+ * Form submission handler for node_revision_delete_confirm().
+ */
function node_revision_delete_confirm_submit($form, &$form_state) {
$node_revision = $form['#node_revision'];
node_revision_delete($node_revision->vid);
diff --git a/modules/node/node.test b/modules/node/node.test
index d789d3c..5c9118e 100644
--- a/modules/node/node.test
+++ b/modules/node/node.test
@@ -55,53 +55,53 @@ class NodeLoadMultipleTestCase extends DrupalWebTestCase {
// Confirm that promoted nodes appear in the default node listing.
$this->drupalGet('node');
- $this->assertText($node1->title, t('Node title appears on the default listing.'));
- $this->assertText($node2->title, t('Node title appears on the default listing.'));
- $this->assertNoText($node3->title, t('Node title does not appear in the default listing.'));
- $this->assertNoText($node4->title, t('Node title does not appear in the default listing.'));
+ $this->assertText($node1->title, 'Node title appears on the default listing.');
+ $this->assertText($node2->title, 'Node title appears on the default listing.');
+ $this->assertNoText($node3->title, 'Node title does not appear in the default listing.');
+ $this->assertNoText($node4->title, 'Node title does not appear in the default listing.');
// Load nodes with only a condition. Nodes 3 and 4 will be loaded.
$nodes = node_load_multiple(NULL, array('promote' => 0));
- $this->assertEqual($node3->title, $nodes[$node3->nid]->title, t('Node was loaded.'));
- $this->assertEqual($node4->title, $nodes[$node4->nid]->title, t('Node was loaded.'));
+ $this->assertEqual($node3->title, $nodes[$node3->nid]->title, 'Node was loaded.');
+ $this->assertEqual($node4->title, $nodes[$node4->nid]->title, 'Node was loaded.');
$count = count($nodes);
- $this->assertTrue($count == 2, t('@count nodes loaded.', array('@count' => $count)));
+ $this->assertTrue($count == 2, format_string('@count nodes loaded.', array('@count' => $count)));
// Load nodes by nid. Nodes 1, 2 and 4 will be loaded.
$nodes = node_load_multiple(array(1, 2, 4));
$count = count($nodes);
- $this->assertTrue(count($nodes) == 3, t('@count nodes loaded', array('@count' => $count)));
- $this->assertTrue(isset($nodes[$node1->nid]), t('Node is correctly keyed in the array'));
- $this->assertTrue(isset($nodes[$node2->nid]), t('Node is correctly keyed in the array'));
- $this->assertTrue(isset($nodes[$node4->nid]), t('Node is correctly keyed in the array'));
+ $this->assertTrue(count($nodes) == 3, format_string('@count nodes loaded', array('@count' => $count)));
+ $this->assertTrue(isset($nodes[$node1->nid]), 'Node is correctly keyed in the array');
+ $this->assertTrue(isset($nodes[$node2->nid]), 'Node is correctly keyed in the array');
+ $this->assertTrue(isset($nodes[$node4->nid]), 'Node is correctly keyed in the array');
foreach ($nodes as $node) {
- $this->assertTrue(is_object($node), t('Node is an object'));
+ $this->assertTrue(is_object($node), 'Node is an object');
}
// Load nodes by nid, where type = article. Nodes 1, 2 and 3 will be loaded.
$nodes = node_load_multiple(array(1, 2, 3, 4), array('type' => 'article'));
$count = count($nodes);
- $this->assertTrue($count == 3, t('@count nodes loaded', array('@count' => $count)));
- $this->assertEqual($nodes[$node1->nid]->title, $node1->title, t('Node successfully loaded.'));
- $this->assertEqual($nodes[$node2->nid]->title, $node2->title, t('Node successfully loaded.'));
- $this->assertEqual($nodes[$node3->nid]->title, $node3->title, t('Node successfully loaded.'));
+ $this->assertTrue($count == 3, format_string('@count nodes loaded', array('@count' => $count)));
+ $this->assertEqual($nodes[$node1->nid]->title, $node1->title, 'Node successfully loaded.');
+ $this->assertEqual($nodes[$node2->nid]->title, $node2->title, 'Node successfully loaded.');
+ $this->assertEqual($nodes[$node3->nid]->title, $node3->title, 'Node successfully loaded.');
$this->assertFalse(isset($nodes[$node4->nid]));
// Now that all nodes have been loaded into the static cache, ensure that
// they are loaded correctly again when a condition is passed.
$nodes = node_load_multiple(array(1, 2, 3, 4), array('type' => 'article'));
$count = count($nodes);
- $this->assertTrue($count == 3, t('@count nodes loaded.', array('@count' => $count)));
- $this->assertEqual($nodes[$node1->nid]->title, $node1->title, t('Node successfully loaded'));
- $this->assertEqual($nodes[$node2->nid]->title, $node2->title, t('Node successfully loaded'));
- $this->assertEqual($nodes[$node3->nid]->title, $node3->title, t('Node successfully loaded'));
- $this->assertFalse(isset($nodes[$node4->nid]), t('Node was not loaded'));
+ $this->assertTrue($count == 3, format_string('@count nodes loaded.', array('@count' => $count)));
+ $this->assertEqual($nodes[$node1->nid]->title, $node1->title, 'Node successfully loaded');
+ $this->assertEqual($nodes[$node2->nid]->title, $node2->title, 'Node successfully loaded');
+ $this->assertEqual($nodes[$node3->nid]->title, $node3->title, 'Node successfully loaded');
+ $this->assertFalse(isset($nodes[$node4->nid]), 'Node was not loaded');
// Load nodes by nid, where type = article and promote = 0.
$nodes = node_load_multiple(array(1, 2, 3, 4), array('type' => 'article', 'promote' => 0));
$count = count($nodes);
- $this->assertTrue($count == 1, t('@count node loaded', array('@count' => $count)));
- $this->assertEqual($nodes[$node3->nid]->title, $node3->title, t('Node successfully loaded.'));
+ $this->assertTrue($count == 1, format_string('@count node loaded', array('@count' => $count)));
+ $this->assertEqual($nodes[$node3->nid]->title, $node3->title, 'Node successfully loaded.');
}
}
@@ -136,21 +136,36 @@ class NodeLoadHooksTestCase extends DrupalWebTestCase {
// reflect the expected values.
$nodes = node_load_multiple(array(), array('status' => NODE_PUBLISHED));
$loaded_node = end($nodes);
- $this->assertEqual($loaded_node->node_test_loaded_nids, array($node1->nid, $node2->nid), t('hook_node_load() received the correct list of node IDs the first time it was called.'));
- $this->assertEqual($loaded_node->node_test_loaded_types, array('article'), t('hook_node_load() received the correct list of node types the first time it was called.'));
+ $this->assertEqual($loaded_node->node_test_loaded_nids, array($node1->nid, $node2->nid), 'hook_node_load() received the correct list of node IDs the first time it was called.');
+ $this->assertEqual($loaded_node->node_test_loaded_types, array('article'), 'hook_node_load() received the correct list of node types the first time it was called.');
// Now, as part of the same page request, load a set of nodes that contain
// both articles and pages, and make sure the parameters passed to
// node_test_node_load() are correctly updated.
$nodes = node_load_multiple(array(), array('status' => NODE_NOT_PUBLISHED));
$loaded_node = end($nodes);
- $this->assertEqual($loaded_node->node_test_loaded_nids, array($node3->nid, $node4->nid), t('hook_node_load() received the correct list of node IDs the second time it was called.'));
- $this->assertEqual($loaded_node->node_test_loaded_types, array('article', 'page'), t('hook_node_load() received the correct list of node types the second time it was called.'));
+ $this->assertEqual($loaded_node->node_test_loaded_nids, array($node3->nid, $node4->nid), 'hook_node_load() received the correct list of node IDs the second time it was called.');
+ $this->assertEqual($loaded_node->node_test_loaded_types, array('article', 'page'), 'hook_node_load() received the correct list of node types the second time it was called.');
}
}
+/**
+ * Tests the node revision functionality.
+ */
class NodeRevisionsTestCase extends DrupalWebTestCase {
+
+ /**
+ * Nodes used by the test.
+ *
+ * @var array
+ */
protected $nodes;
+
+ /**
+ * The revision messages for node revisions created in the test.
+ *
+ * @var array
+ */
protected $logs;
public static function getInfo() {
@@ -198,7 +213,7 @@ class NodeRevisionsTestCase extends DrupalWebTestCase {
}
/**
- * Check node revision related operations.
+ * Checks node revision related operations.
*/
function testRevisions() {
$nodes = $this->nodes;
@@ -209,28 +224,28 @@ class NodeRevisionsTestCase extends DrupalWebTestCase {
// Confirm the correct revision text appears on "view revisions" page.
$this->drupalGet("node/$node->nid/revisions/$node->vid/view");
- $this->assertText($node->body[LANGUAGE_NONE][0]['value'], t('Correct text displays for version.'));
+ $this->assertText($node->body[LANGUAGE_NONE][0]['value'], 'Correct text displays for version.');
// Confirm the correct log message appears on "revisions overview" page.
$this->drupalGet("node/$node->nid/revisions");
foreach ($logs as $log) {
- $this->assertText($log, t('Log message found.'));
+ $this->assertText($log, 'Log message found.');
}
// Confirm that revisions revert properly.
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/revert", array(), t('Revert'));
$this->assertRaw(t('@type %title has been reverted back to the revision from %revision-date.',
array('@type' => 'Basic page', '%title' => $nodes[1]->title,
- '%revision-date' => format_date($nodes[1]->revision_timestamp))), t('Revision reverted.'));
+ '%revision-date' => format_date($nodes[1]->revision_timestamp))), 'Revision reverted.');
$reverted_node = node_load($node->nid);
- $this->assertTrue(($nodes[1]->body[LANGUAGE_NONE][0]['value'] == $reverted_node->body[LANGUAGE_NONE][0]['value']), t('Node reverted correctly.'));
+ $this->assertTrue(($nodes[1]->body[LANGUAGE_NONE][0]['value'] == $reverted_node->body[LANGUAGE_NONE][0]['value']), 'Node reverted correctly.');
// Confirm revisions delete properly.
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/delete", array(), t('Delete'));
$this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.',
array('%revision-date' => format_date($nodes[1]->revision_timestamp),
- '@type' => 'Basic page', '%title' => $nodes[1]->title)), t('Revision deleted.'));
- $this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid and vid = :vid', array(':nid' => $node->nid, ':vid' => $nodes[1]->vid))->fetchField() == 0, t('Revision not found.'));
+ '@type' => 'Basic page', '%title' => $nodes[1]->title)), 'Revision deleted.');
+ $this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid and vid = :vid', array(':nid' => $node->nid, ':vid' => $nodes[1]->vid))->fetchField() == 0, 'Revision not found.');
}
/**
@@ -256,9 +271,9 @@ class NodeRevisionsTestCase extends DrupalWebTestCase {
);
node_save($updated_node);
$this->drupalGet('node/' . $node->nid);
- $this->assertText($new_title, t('New node title appears on the page.'));
+ $this->assertText($new_title, 'New node title appears on the page.');
$node_revision = node_load($node->nid, NULL, TRUE);
- $this->assertEqual($node_revision->log, $log, t('After an existing node revision is re-saved without a log message, the original log message is preserved.'));
+ $this->assertEqual($node_revision->log, $log, 'After an existing node revision is re-saved without a log message, the original log message is preserved.');
// Create another node with an initial log message.
$node = $this->drupalCreateNode(array('log' => $log));
@@ -282,8 +297,23 @@ class NodeRevisionsTestCase extends DrupalWebTestCase {
}
}
+/**
+ * Tests the node edit functionality.
+ */
class PageEditTestCase extends DrupalWebTestCase {
+
+ /**
+ * A user with permission to create and edit own page content.
+ *
+ * @var object
+ */
protected $web_user;
+
+ /**
+ * A user with permission to bypass node access and administer nodes.
+ *
+ * @var object
+ */
protected $admin_user;
public static function getInfo() {
@@ -302,7 +332,7 @@ class PageEditTestCase extends DrupalWebTestCase {
}
/**
- * Check node edit functionality.
+ * Checks node edit functionality.
*/
function testPageEdit() {
$this->drupalLogin($this->web_user);
@@ -318,20 +348,20 @@ class PageEditTestCase extends DrupalWebTestCase {
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
+ $this->assertTrue($node, 'Node found in database.');
// Check that "edit" link points to correct page.
$this->clickLink(t('Edit'));
$edit_url = url("node/$node->nid/edit", array('absolute' => TRUE));
$actual_url = $this->getURL();
- $this->assertEqual($edit_url, $actual_url, t('On edit page.'));
+ $this->assertEqual($edit_url, $actual_url, 'On edit page.');
// Check that the title and body fields are displayed with the correct values.
$active = '' . t('(active tab)') . '';
$link_text = t('!local-task-title!active', array('!local-task-title' => t('Edit'), '!active' => $active));
- $this->assertText(strip_tags($link_text), 0, t('Edit tab found and marked active.'));
- $this->assertFieldByName($title_key, $edit[$title_key], t('Title field displayed.'));
- $this->assertFieldByName($body_key, $edit[$body_key], t('Body field displayed.'));
+ $this->assertText(strip_tags($link_text), 0, 'Edit tab found and marked active.');
+ $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
+ $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
// Edit the content of the node.
$edit = array();
@@ -341,8 +371,8 @@ class PageEditTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, $edit, t('Save'));
// Check that the title and body fields are displayed with the updated values.
- $this->assertText($edit[$title_key], t('Title displayed.'));
- $this->assertText($edit[$body_key], t('Body displayed.'));
+ $this->assertText($edit[$title_key], 'Title displayed.');
+ $this->assertText($edit[$body_key], 'Body displayed.');
// Login as a second administrator user.
$second_web_user = $this->drupalCreateUser(array('administer nodes', 'edit any page content'));
@@ -369,7 +399,7 @@ class PageEditTestCase extends DrupalWebTestCase {
}
/**
- * Check changing node authored by fields.
+ * Tests changing a node's "authored by" field.
*/
function testPageAuthoredBy() {
$this->drupalLogin($this->admin_user);
@@ -414,6 +444,9 @@ class PageEditTestCase extends DrupalWebTestCase {
}
}
+/**
+ * Tests the node entity preview functionality.
+ */
class PagePreviewTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -431,7 +464,7 @@ class PagePreviewTestCase extends DrupalWebTestCase {
}
/**
- * Check the node preview functionality.
+ * Checks the node preview functionality.
*/
function testPagePreview() {
$langcode = LANGUAGE_NONE;
@@ -445,17 +478,17 @@ class PagePreviewTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/page', $edit, t('Preview'));
// Check that the preview is displaying the title and body.
- $this->assertTitle(t('Preview | Drupal'), t('Basic page title is preview.'));
- $this->assertText($edit[$title_key], t('Title displayed.'));
- $this->assertText($edit[$body_key], t('Body displayed.'));
+ $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
+ $this->assertText($edit[$title_key], 'Title displayed.');
+ $this->assertText($edit[$body_key], 'Body displayed.');
// Check that the title and body fields are displayed with the correct values.
- $this->assertFieldByName($title_key, $edit[$title_key], t('Title field displayed.'));
- $this->assertFieldByName($body_key, $edit[$body_key], t('Body field displayed.'));
+ $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
+ $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
}
/**
- * Check the node preview functionality, when using revisions.
+ * Checks the node preview functionality, when using revisions.
*/
function testPagePreviewWithRevisions() {
$langcode = LANGUAGE_NONE;
@@ -472,19 +505,22 @@ class PagePreviewTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/page', $edit, t('Preview'));
// Check that the preview is displaying the title and body.
- $this->assertTitle(t('Preview | Drupal'), t('Basic page title is preview.'));
- $this->assertText($edit[$title_key], t('Title displayed.'));
- $this->assertText($edit[$body_key], t('Body displayed.'));
+ $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
+ $this->assertText($edit[$title_key], 'Title displayed.');
+ $this->assertText($edit[$body_key], 'Body displayed.');
// Check that the title and body fields are displayed with the correct values.
- $this->assertFieldByName($title_key, $edit[$title_key], t('Title field displayed.'));
- $this->assertFieldByName($body_key, $edit[$body_key], t('Body field displayed.'));
+ $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
+ $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
// Check that the log field has the correct value.
- $this->assertFieldByName('log', $edit['log'], t('Log field displayed.'));
+ $this->assertFieldByName('log', $edit['log'], 'Log field displayed.');
}
}
+/**
+ * Tests creating and saving a node.
+ */
class NodeCreationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -503,7 +539,7 @@ class NodeCreationTestCase extends DrupalWebTestCase {
}
/**
- * Create a "Basic page" node and verify its consistency in the database.
+ * Creates a "Basic page" node and verifies its consistency in the database.
*/
function testNodeCreation() {
// Create a node.
@@ -514,15 +550,15 @@ class NodeCreationTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/page', $edit, t('Save'));
// Check that the Basic page has been created.
- $this->assertRaw(t('!post %title has been created.', array('!post' => 'Basic page', '%title' => $edit["title"])), t('Basic page created.'));
+ $this->assertRaw(t('!post %title has been created.', array('!post' => 'Basic page', '%title' => $edit["title"])), 'Basic page created.');
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit["title"]);
- $this->assertTrue($node, t('Node found in database.'));
+ $this->assertTrue($node, 'Node found in database.');
}
/**
- * Create a page node and verify that a transaction rolls back the failed creation
+ * Verifies that a transaction rolls back the failed creation.
*/
function testFailedPageCreation() {
// Create a node.
@@ -535,6 +571,8 @@ class NodeCreationTestCase extends DrupalWebTestCase {
);
try {
+ // An exception is generated by node_test_exception_node_insert() if the
+ // title is 'testing_transaction_exception'.
node_save((object) $edit);
$this->fail(t('Expected exception has not been thrown.'));
}
@@ -545,24 +583,46 @@ class NodeCreationTestCase extends DrupalWebTestCase {
if (Database::getConnection()->supportsTransactions()) {
// Check that the node does not exist in the database.
$node = $this->drupalGetNodeByTitle($edit['title']);
- $this->assertFalse($node, t('Transactions supported, and node not found in database.'));
+ $this->assertFalse($node, 'Transactions supported, and node not found in database.');
}
else {
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit['title']);
- $this->assertTrue($node, t('Transactions not supported, and node found in database.'));
+ $this->assertTrue($node, 'Transactions not supported, and node found in database.');
// Check that the failed rollback was logged.
$records = db_query("SELECT wid FROM {watchdog} WHERE message LIKE 'Explicit rollback failed%'")->fetchAll();
- $this->assertTrue(count($records) > 0, t('Transactions not supported, and rollback error logged to watchdog.'));
+ $this->assertTrue(count($records) > 0, 'Transactions not supported, and rollback error logged to watchdog.');
}
// Check that the rollback error was logged.
$records = db_query("SELECT wid FROM {watchdog} WHERE variables LIKE '%Test exception for rollback.%'")->fetchAll();
- $this->assertTrue(count($records) > 0, t('Rollback explanatory error logged to watchdog.'));
+ $this->assertTrue(count($records) > 0, 'Rollback explanatory error logged to watchdog.');
+ }
+
+ /**
+ * Create an unpublished node and confirm correct redirect behavior.
+ */
+ function testUnpublishedNodeCreation() {
+ // Set "Basic page" content type to be unpublished by default.
+ variable_set('node_options_page', array());
+ // Set the front page to the default "node" page.
+ variable_set('site_frontpage', 'node');
+
+ // Create a node.
+ $edit = array();
+ $edit["title"] = $this->randomName(8);
+ $edit["body[" . LANGUAGE_NONE . "][0][value]"] = $this->randomName(16);
+ $this->drupalPost('node/add/page', $edit, t('Save'));
+
+ // Check that the user was redirected to the home page.
+ $this->assertText(t('Welcome to Drupal'), t('The user is redirected to the home page.'));
}
}
+/**
+ * Tests the functionality of node entity edit permissions.
+ */
class PageViewTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -573,12 +633,12 @@ class PageViewTestCase extends DrupalWebTestCase {
}
/**
- * Creates a node and then an anonymous and unpermissioned user attempt to edit the node.
+ * Tests an anonymous and unpermissioned user attempting to edit the node.
*/
function testPageView() {
// Create a node to view.
$node = $this->drupalCreateNode();
- $this->assertTrue(node_load($node->nid), t('Node created.'));
+ $this->assertTrue(node_load($node->nid), 'Node created.');
// Try to edit with anonymous user.
$html = $this->drupalGet("node/$node->nid/edit");
@@ -602,6 +662,9 @@ class PageViewTestCase extends DrupalWebTestCase {
}
}
+/**
+ * Tests the summary length functionality.
+ */
class SummaryLengthTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -612,7 +675,7 @@ class SummaryLengthTestCase extends DrupalWebTestCase {
}
/**
- * Creates a node and then an anonymous and unpermissioned user attempt to edit the node.
+ * Tests the node summary length functionality.
*/
function testSummaryLength() {
// Create a node to view.
@@ -621,7 +684,7 @@ class SummaryLengthTestCase extends DrupalWebTestCase {
'promote' => 1,
);
$node = $this->drupalCreateNode($settings);
- $this->assertTrue(node_load($node->nid), t('Node created.'));
+ $this->assertTrue(node_load($node->nid), 'Node created.');
// Create user with permission to view the node.
$web_user = $this->drupalCreateUser(array('access content', 'administer content types'));
@@ -631,7 +694,7 @@ class SummaryLengthTestCase extends DrupalWebTestCase {
$this->drupalGet("node");
// The node teaser when it has 600 characters in length
$expected = 'What is a Drupalism?';
- $this->assertRaw($expected, t('Check that the summary is 600 characters in length'), 'Node');
+ $this->assertRaw($expected, 'Check that the summary is 600 characters in length', 'Node');
// Change the teaser length for "Basic page" content type.
$instance = field_info_instance('node', 'body', $node->type);
@@ -640,10 +703,13 @@ class SummaryLengthTestCase extends DrupalWebTestCase {
// Attempt to access the front page again and check if the summary is now only 200 characters in length.
$this->drupalGet("node");
- $this->assertNoRaw($expected, t('Check that the summary is not longer than 200 characters'), 'Node');
+ $this->assertNoRaw($expected, 'Check that the summary is not longer than 200 characters', 'Node');
}
}
+/**
+ * Tests XSS functionality with a node entity.
+ */
class NodeTitleXSSTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -653,6 +719,9 @@ class NodeTitleXSSTestCase extends DrupalWebTestCase {
);
}
+ /**
+ * Tests XSS functionality with a node entity.
+ */
function testNodeTitleXSS() {
// Prepare a user to do the stuff.
$web_user = $this->drupalCreateUser(array('create page content', 'edit any page content'));
@@ -663,21 +732,24 @@ class NodeTitleXSSTestCase extends DrupalWebTestCase {
$edit = array("title" => $title);
$this->drupalPost('node/add/page', $edit, t('Preview'));
- $this->assertNoRaw($xss, t('Harmful tags are escaped when previewing a node.'));
+ $this->assertNoRaw($xss, 'Harmful tags are escaped when previewing a node.');
$settings = array('title' => $title);
$node = $this->drupalCreateNode($settings);
$this->drupalGet('node/' . $node->nid);
// assertTitle() decodes HTML-entities inside the element.
- $this->assertTitle($edit["title"] . ' | Drupal', t('Title is diplayed when viewing a node.'));
- $this->assertNoRaw($xss, t('Harmful tags are escaped when viewing a node.'));
+ $this->assertTitle($edit["title"] . ' | Drupal', 'Title is diplayed when viewing a node.');
+ $this->assertNoRaw($xss, 'Harmful tags are escaped when viewing a node.');
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertNoRaw($xss, t('Harmful tags are escaped when editing a node.'));
+ $this->assertNoRaw($xss, 'Harmful tags are escaped when editing a node.');
}
}
+/**
+ * Tests the availability of the syndicate block.
+ */
class NodeBlockTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@@ -695,21 +767,24 @@ class NodeBlockTestCase extends DrupalWebTestCase {
$this->drupalLogin($admin_user);
}
+ /**
+ * Tests that the "Syndicate" block is shown when enabled.
+ */
function testSyndicateBlock() {
// Set block title to confirm that the interface is available.
$this->drupalPost('admin/structure/block/manage/node/syndicate/configure', array('title' => $this->randomName(8)), t('Save block'));
- $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.'));
+ $this->assertText(t('The block configuration has been saved.'), 'Block configuration set.');
// Set the block to a region to confirm block is available.
$edit = array();
$edit['blocks[node_syndicate][region]'] = 'footer';
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
- $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.'));
+ $this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.');
}
}
/**
- * Check that the post information displays when enabled for a content type.
+ * Checks that the post information displays when enabled for a content type.
*/
class NodePostSettingsTestCase extends DrupalWebTestCase {
public static function getInfo() {
@@ -728,7 +803,7 @@ class NodePostSettingsTestCase extends DrupalWebTestCase {
}
/**
- * Set "Basic page" content type to display post information and confirm its presence on a new node.
+ * Confirms "Basic page" content type and post information is on a new node.
*/
function testPagePostInfo() {
@@ -747,11 +822,11 @@ class NodePostSettingsTestCase extends DrupalWebTestCase {
// Check that the post information is displayed.
$node = $this->drupalGetNodeByTitle($edit["title"]);
$elements = $this->xpath('//div[contains(@class,:class)]', array(':class' => 'submitted'));
- $this->assertEqual(count($elements), 1, t('Post information is displayed.'));
+ $this->assertEqual(count($elements), 1, 'Post information is displayed.');
}
/**
- * Set "Basic page" content type to not display post information and confirm its absence on a new node.
+ * Confirms absence of post information on a new node.
*/
function testPageNotPostInfo() {
@@ -769,12 +844,12 @@ class NodePostSettingsTestCase extends DrupalWebTestCase {
// Check that the post information is displayed.
$node = $this->drupalGetNodeByTitle($edit["title"]);
- $this->assertNoRaw('', t('Post information is not displayed.'));
+ $this->assertNoRaw('', 'Post information is not displayed.');
}
}
/**
- * Ensure that data added to nodes by other modules appears in RSS feeds.
+ * Ensures that data added to nodes by other modules appears in RSS feeds.
*
* Create a node, enable the node_test module to ensure that extra data is
* added to the node->content array, then verify that the data appears on the
@@ -801,8 +876,7 @@ class NodeRSSContentTestCase extends DrupalWebTestCase {
}
/**
- * Create a new node and ensure that it includes the custom data when added
- * to an RSS feed.
+ * Ensures that a new node includes the custom data when added to an RSS feed.
*/
function testNodeRSSContent() {
// Create a node.
@@ -812,12 +886,12 @@ class NodeRSSContentTestCase extends DrupalWebTestCase {
// Check that content added in 'rss' view mode appear in RSS feed.
$rss_only_content = t('Extra data that should appear only in the RSS feed for node !nid.', array('!nid' => $node->nid));
- $this->assertText($rss_only_content, t('Node content designated for RSS appear in RSS feed.'));
+ $this->assertText($rss_only_content, 'Node content designated for RSS appear in RSS feed.');
// Check that content added in view modes other than 'rss' doesn't
// appear in RSS feed.
$non_rss_content = t('Extra data that should appear everywhere except the RSS feed for node !nid.', array('!nid' => $node->nid));
- $this->assertNoText($non_rss_content, t('Node content not designed for RSS doesn\'t appear in RSS feed.'));
+ $this->assertNoText($non_rss_content, 'Node content not designed for RSS doesn\'t appear in RSS feed.');
// Check that extra RSS elements and namespaces are added to RSS feed.
$test_element = array(
@@ -825,25 +899,27 @@ class NodeRSSContentTestCase extends DrupalWebTestCase {
'value' => t('Value of testElement RSS element for node !nid.', array('!nid' => $node->nid)),
);
$test_ns = 'xmlns:drupaltest="http://example.com/test-namespace"';
- $this->assertRaw(format_xml_elements(array($test_element)), t('Extra RSS elements appear in RSS feed.'));
- $this->assertRaw($test_ns, t('Extra namespaces appear in RSS feed.'));
+ $this->assertRaw(format_xml_elements(array($test_element)), 'Extra RSS elements appear in RSS feed.');
+ $this->assertRaw($test_ns, 'Extra namespaces appear in RSS feed.');
// Check that content added in 'rss' view mode doesn't appear when
// viewing node.
$this->drupalGet("node/$node->nid");
- $this->assertNoText($rss_only_content, t('Node content designed for RSS doesn\'t appear when viewing node.'));
-
+ $this->assertNoText($rss_only_content, 'Node content designed for RSS doesn\'t appear when viewing node.');
+
// Check that the node feed page does not try to interpret additional path
// components as arguments for node_feed() and returns default content.
$this->drupalGet('rss.xml/' . $this->randomName() . '/' . $this->randomName());
- $this->assertText($rss_only_content, t('Ignore page arguments when delivering rss.xml.'));
+ $this->assertText($rss_only_content, 'Ignore page arguments when delivering rss.xml.');
}
}
/**
- * Test case to verify basic node_access functionality.
+ * Tests basic node_access functionality.
+ *
+ * Note that hook_node_access_records() is covered in another test class.
+ *
* @todo Cover hook_node_access in a separate test class.
- * hook_node_access_records is covered in another test class.
*/
class NodeAccessTestCase extends DrupalWebTestCase {
public static function getInfo() {
@@ -855,11 +931,11 @@ class NodeAccessTestCase extends DrupalWebTestCase {
}
/**
- * Asserts node_access correctly grants or denies access.
+ * Asserts node_access() correctly grants or denies access.
*/
function assertNodeAccess($ops, $node, $account) {
foreach ($ops as $op => $result) {
- $msg = t("node_access returns @result with operation '@op'.", array('@result' => $result ? 'true' : 'false', '@op' => $op));
+ $msg = format_string("node_access returns @result with operation '@op'.", array('@result' => $result ? 'true' : 'false', '@op' => $op));
$this->assertEqual($result, node_access($op, $node, $account), $msg);
}
}
@@ -910,7 +986,7 @@ class NodeAccessTestCase extends DrupalWebTestCase {
}
/**
- * Test case to verify hook_node_access_records functionality.
+ * Tests hook_node_access_records() functionality.
*/
class NodeAccessRecordsTestCase extends DrupalWebTestCase {
public static function getInfo() {
@@ -929,49 +1005,49 @@ class NodeAccessRecordsTestCase extends DrupalWebTestCase {
}
/**
- * Create a node and test the creation of node access rules.
+ * Creates a node and tests the creation of node access rules.
*/
function testNodeAccessRecords() {
// Create an article node.
$node1 = $this->drupalCreateNode(array('type' => 'article'));
- $this->assertTrue(node_load($node1->nid), t('Article node created.'));
+ $this->assertTrue(node_load($node1->nid), 'Article node created.');
// Check to see if grants added by node_test_node_access_records made it in.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node1->nid))->fetchAll();
- $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
- $this->assertEqual($records[0]->realm, 'test_article_realm', t('Grant with article_realm acquired for node without alteration.'));
- $this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
+ $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
+ $this->assertEqual($records[0]->realm, 'test_article_realm', 'Grant with article_realm acquired for node without alteration.');
+ $this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Create an unpromoted "Basic page" node.
$node2 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 0));
- $this->assertTrue(node_load($node2->nid), t('Unpromoted basic page node created.'));
+ $this->assertTrue(node_load($node2->nid), 'Unpromoted basic page node created.');
// Check to see if grants added by node_test_node_access_records made it in.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node2->nid))->fetchAll();
- $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
- $this->assertEqual($records[0]->realm, 'test_page_realm', t('Grant with page_realm acquired for node without alteration.'));
- $this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
+ $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
+ $this->assertEqual($records[0]->realm, 'test_page_realm', 'Grant with page_realm acquired for node without alteration.');
+ $this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Create an unpromoted, unpublished "Basic page" node.
$node3 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 0, 'status' => 0));
- $this->assertTrue(node_load($node3->nid), t('Unpromoted, unpublished basic page node created.'));
+ $this->assertTrue(node_load($node3->nid), 'Unpromoted, unpublished basic page node created.');
// Check to see if grants added by node_test_node_access_records made it in.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node3->nid))->fetchAll();
- $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
- $this->assertEqual($records[0]->realm, 'test_page_realm', t('Grant with page_realm acquired for node without alteration.'));
- $this->assertEqual($records[0]->gid, 1, t('Grant with gid = 1 acquired for node without alteration.'));
+ $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
+ $this->assertEqual($records[0]->realm, 'test_page_realm', 'Grant with page_realm acquired for node without alteration.');
+ $this->assertEqual($records[0]->gid, 1, 'Grant with gid = 1 acquired for node without alteration.');
// Create a promoted "Basic page" node.
$node4 = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
- $this->assertTrue(node_load($node4->nid), t('Promoted basic page node created.'));
+ $this->assertTrue(node_load($node4->nid), 'Promoted basic page node created.');
// Check to see if grant added by node_test_node_access_records was altered
// by node_test_node_access_records_alter.
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node4->nid))->fetchAll();
- $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
- $this->assertEqual($records[0]->realm, 'test_alter_realm', t('Altered grant with alter_realm acquired for node.'));
- $this->assertEqual($records[0]->gid, 2, t('Altered grant with gid = 2 acquired for node.'));
+ $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
+ $this->assertEqual($records[0]->realm, 'test_alter_realm', 'Altered grant with alter_realm acquired for node.');
+ $this->assertEqual($records[0]->gid, 2, 'Altered grant with gid = 2 acquired for node.');
// Check to see if we can alter grants with hook_node_grants_alter().
$operations = array('view', 'update', 'delete');
@@ -981,14 +1057,14 @@ class NodeAccessRecordsTestCase extends DrupalWebTestCase {
$grants = node_test_node_grants($op, $web_user);
$altered_grants = $grants;
drupal_alter('node_grants', $altered_grants, $web_user, $op);
- $this->assertNotEqual($grants, $altered_grants, t('Altered the %op grant for a user.', array('%op' => $op)));
+ $this->assertNotEqual($grants, $altered_grants, format_string('Altered the %op grant for a user.', array('%op' => $op)));
}
// Check that core does not grant access to an unpublished node when an
// empty $grants array is returned.
$node6 = $this->drupalCreateNode(array('status' => 0, 'disable_node_access' => TRUE));
$records = db_query('SELECT realm, gid FROM {node_access} WHERE nid = :nid', array(':nid' => $node6->nid))->fetchAll();
- $this->assertEqual(count($records), 0, t('Returned no records for unpublished node.'));
+ $this->assertEqual(count($records), 0, 'Returned no records for unpublished node.');
}
}
@@ -1005,9 +1081,6 @@ class NodeAccessBaseTableTestCase extends DrupalWebTestCase {
);
}
- /**
- * Enable modules and create user with specific permissions.
- */
public function setUp() {
parent::setUp('node_access_test');
node_access_rebuild();
@@ -1015,7 +1088,7 @@ class NodeAccessBaseTableTestCase extends DrupalWebTestCase {
}
/**
- * Test the "private" node access.
+ * Tests the "private" node access functionality.
*
* - Create 2 users with "access content" and "create article" permissions.
* - Each user creates one private and one not private article.
@@ -1058,7 +1131,7 @@ class NodeAccessBaseTableTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/article', $edit, t('Save'));
$nid = db_query('SELECT nid FROM {node} WHERE title = :title', array(':title' => $edit['title']))->fetchField();
$private_status = db_query('SELECT private FROM {node_access_test} where nid = :nid', array(':nid' => $nid))->fetchField();
- $this->assertTrue($is_private == $private_status, t('The private status of the node was properly set in the node_access_test table.'));
+ $this->assertTrue($is_private == $private_status, 'The private status of the node was properly set in the node_access_test table.');
if ($is_private) {
$private_nodes[] = $nid;
}
@@ -1068,8 +1141,8 @@ class NodeAccessBaseTableTestCase extends DrupalWebTestCase {
}
$this->publicTid = db_query('SELECT tid FROM {taxonomy_term_data} WHERE name = :name', array(':name' => 'public'))->fetchField();
$this->privateTid = db_query('SELECT tid FROM {taxonomy_term_data} WHERE name = :name', array(':name' => 'private'))->fetchField();
- $this->assertTrue($this->publicTid, t('Public tid was found'));
- $this->assertTrue($this->privateTid, t('Private tid was found'));
+ $this->assertTrue($this->publicTid, 'Public tid was found');
+ $this->assertTrue($this->privateTid, 'Private tid was found');
foreach ($simple_users as $this->webUser) {
$this->drupalLogin($this->webUser);
// Check own nodes to see that all are readable.
@@ -1152,7 +1225,7 @@ class NodeAccessBaseTableTestCase extends DrupalWebTestCase {
}
/**
- * Test case to check node save related functionality, including import-save
+ * Tests node save related functionality, including import-save.
*/
class NodeSaveTestCase extends DrupalWebTestCase {
@@ -1173,7 +1246,8 @@ class NodeSaveTestCase extends DrupalWebTestCase {
}
/**
- * Import test, to check if custom node ids are saved properly.
+ * Checks whether custom node IDs are saved properly during an import operation.
+ *
* Workflow:
* - first create a piece of content
* - save the content
@@ -1195,20 +1269,19 @@ class NodeSaveTestCase extends DrupalWebTestCase {
$node = node_submit((object) $node);
// Verify that node_submit did not overwrite the user ID.
- $this->assertEqual($node->uid, $this->web_user->uid, t('Function node_submit() preserves user ID'));
+ $this->assertEqual($node->uid, $this->web_user->uid, 'Function node_submit() preserves user ID');
node_save($node);
// Test the import.
$node_by_nid = node_load($test_nid);
- $this->assertTrue($node_by_nid, t('Node load by node ID.'));
+ $this->assertTrue($node_by_nid, 'Node load by node ID.');
$node_by_title = $this->drupalGetNodeByTitle($title);
- $this->assertTrue($node_by_title, t('Node load by node title.'));
+ $this->assertTrue($node_by_title, 'Node load by node title.');
}
/**
- * Check that the "created" and "changed" timestamps are set correctly when
- * saving a new node or updating an existing node.
+ * Verifies accuracy of the "created" and "changed" timestamp functionality.
*/
function testTimestamps() {
// Use the default timestamps.
@@ -1220,8 +1293,8 @@ class NodeSaveTestCase extends DrupalWebTestCase {
node_save((object) $edit);
$node = $this->drupalGetNodeByTitle($edit['title']);
- $this->assertEqual($node->created, REQUEST_TIME, t('Creating a node sets default "created" timestamp.'));
- $this->assertEqual($node->changed, REQUEST_TIME, t('Creating a node sets default "changed" timestamp.'));
+ $this->assertEqual($node->created, REQUEST_TIME, 'Creating a node sets default "created" timestamp.');
+ $this->assertEqual($node->changed, REQUEST_TIME, 'Creating a node sets default "changed" timestamp.');
// Store the timestamps.
$created = $node->created;
@@ -1229,15 +1302,15 @@ class NodeSaveTestCase extends DrupalWebTestCase {
node_save($node);
$node = $this->drupalGetNodeByTitle($edit['title'], TRUE);
- $this->assertEqual($node->created, $created, t('Updating a node preserves "created" timestamp.'));
+ $this->assertEqual($node->created, $created, 'Updating a node preserves "created" timestamp.');
// Programmatically set the timestamps using hook_node_presave.
$node->title = 'testing_node_presave';
node_save($node);
$node = $this->drupalGetNodeByTitle('testing_node_presave', TRUE);
- $this->assertEqual($node->created, 280299600, t('Saving a node uses "created" timestamp set in presave hook.'));
- $this->assertEqual($node->changed, 979534800, t('Saving a node uses "changed" timestamp set in presave hook.'));
+ $this->assertEqual($node->created, 280299600, 'Saving a node uses "created" timestamp set in presave hook.');
+ $this->assertEqual($node->changed, 979534800, 'Saving a node uses "changed" timestamp set in presave hook.');
// Programmatically set the timestamps on the node.
$edit = array(
@@ -1250,8 +1323,8 @@ class NodeSaveTestCase extends DrupalWebTestCase {
node_save((object) $edit);
$node = $this->drupalGetNodeByTitle($edit['title']);
- $this->assertEqual($node->created, 280299600, t('Creating a node uses user-set "created" timestamp.'));
- $this->assertNotEqual($node->changed, 979534800, t('Creating a node doesn\'t use user-set "changed" timestamp.'));
+ $this->assertEqual($node->created, 280299600, 'Creating a node uses user-set "created" timestamp.');
+ $this->assertNotEqual($node->changed, 979534800, 'Creating a node doesn\'t use user-set "changed" timestamp.');
// Update the timestamps.
$node->created = 979534800;
@@ -1259,8 +1332,8 @@ class NodeSaveTestCase extends DrupalWebTestCase {
node_save($node);
$node = $this->drupalGetNodeByTitle($edit['title'], TRUE);
- $this->assertEqual($node->created, 979534800, t('Updating a node uses user-set "created" timestamp.'));
- $this->assertNotEqual($node->changed, 280299600, t('Updating a node doesn\'t use user-set "changed" timestamp.'));
+ $this->assertEqual($node->created, 979534800, 'Updating a node uses user-set "created" timestamp.');
+ $this->assertNotEqual($node->changed, 280299600, 'Updating a node doesn\'t use user-set "changed" timestamp.');
}
/**
@@ -1292,6 +1365,22 @@ class NodeSaveTestCase extends DrupalWebTestCase {
$node = node_load($node->nid);
$this->assertEqual($node->title, 'updated_presave', 'Static cache has been cleared.');
}
+
+ /**
+ * Tests saving a node on node insert.
+ *
+ * This test ensures that a node has been fully saved when hook_node_insert()
+ * is invoked, so that the node can be saved again in a hook implementation
+ * without errors.
+ *
+ * @see node_test_node_insert()
+ */
+ function testNodeSaveOnInsert() {
+ // node_test_node_insert() triggers a save on insert if the title equals
+ // 'new'.
+ $node = $this->drupalCreateNode(array('title' => 'new'));
+ $this->assertEqual($node->title, 'Node ' . $node->nid, 'Node saved on node insert.');
+ }
}
/**
@@ -1307,7 +1396,7 @@ class NodeTypeTestCase extends DrupalWebTestCase {
}
/**
- * Ensure that node type functions (node_type_get_*) work correctly.
+ * Ensures that node type functions (node_type_get_*) work correctly.
*
* Load available node types and validate the returned data.
*/
@@ -1315,18 +1404,18 @@ class NodeTypeTestCase extends DrupalWebTestCase {
$node_types = node_type_get_types();
$node_names = node_type_get_names();
- $this->assertTrue(isset($node_types['article']), t('Node type article is available.'));
- $this->assertTrue(isset($node_types['page']), t('Node type basic page is available.'));
+ $this->assertTrue(isset($node_types['article']), 'Node type article is available.');
+ $this->assertTrue(isset($node_types['page']), 'Node type basic page is available.');
- $this->assertEqual($node_types['article']->name, $node_names['article'], t('Correct node type base has been returned.'));
+ $this->assertEqual($node_types['article']->name, $node_names['article'], 'Correct node type base has been returned.');
- $this->assertEqual($node_types['article'], node_type_get_type('article'), t('Correct node type has been returned.'));
- $this->assertEqual($node_types['article']->name, node_type_get_name('article'), t('Correct node type name has been returned.'));
- $this->assertEqual($node_types['page']->base, node_type_get_base('page'), t('Correct node type base has been returned.'));
+ $this->assertEqual($node_types['article'], node_type_get_type('article'), 'Correct node type has been returned.');
+ $this->assertEqual($node_types['article']->name, node_type_get_name('article'), 'Correct node type name has been returned.');
+ $this->assertEqual($node_types['page']->base, node_type_get_base('page'), 'Correct node type base has been returned.');
}
/**
- * Test creating a content type programmatically and via a form.
+ * Tests creating a content type programmatically and via a form.
*/
function testNodeTypeCreation() {
// Create a content type programmaticaly.
@@ -1356,19 +1445,19 @@ class NodeTypeTestCase extends DrupalWebTestCase {
}
/**
- * Test editing a node type using the UI.
+ * Tests editing a node type using the UI.
*/
function testNodeTypeEditing() {
$web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types'));
$this->drupalLogin($web_user);
$instance = field_info_instance('node', 'body', 'page');
- $this->assertEqual($instance['label'], 'Body', t('Body field was found.'));
+ $this->assertEqual($instance['label'], 'Body', 'Body field was found.');
// Verify that title and body fields are displayed.
$this->drupalGet('node/add/page');
- $this->assertRaw('Title', t('Title field was found.'));
- $this->assertRaw('Body', t('Body field was found.'));
+ $this->assertRaw('Title', 'Title field was found.');
+ $this->assertRaw('Body', 'Body field was found.');
// Rename the title field.
$edit = array(
@@ -1379,8 +1468,8 @@ class NodeTypeTestCase extends DrupalWebTestCase {
field_info_cache_clear();
$this->drupalGet('node/add/page');
- $this->assertRaw('Foo', t('New title label was displayed.'));
- $this->assertNoRaw('Title', t('Old title label was not displayed.'));
+ $this->assertRaw('Foo', 'New title label was displayed.');
+ $this->assertNoRaw('Title', 'Old title label was not displayed.');
// Change the name, machine name and description.
$edit = array(
@@ -1392,12 +1481,12 @@ class NodeTypeTestCase extends DrupalWebTestCase {
field_info_cache_clear();
$this->drupalGet('node/add');
- $this->assertRaw('Bar', t('New name was displayed.'));
- $this->assertRaw('Lorem ipsum', t('New description was displayed.'));
+ $this->assertRaw('Bar', 'New name was displayed.');
+ $this->assertRaw('Lorem ipsum', 'New description was displayed.');
$this->clickLink('Bar');
- $this->assertEqual(url('node/add/bar', array('absolute' => TRUE)), $this->getUrl(), t('New machine name was used in URL.'));
- $this->assertRaw('Foo', t('Title field was found.'));
- $this->assertRaw('Body', t('Body field was found.'));
+ $this->assertEqual(url('node/add/bar', array('absolute' => TRUE)), $this->getUrl(), 'New machine name was used in URL.');
+ $this->assertRaw('Foo', 'Title field was found.');
+ $this->assertRaw('Body', 'Body field was found.');
// Remove the body field.
$this->drupalPost('admin/structure/types/manage/bar/fields/body/delete', NULL, t('Delete'));
@@ -1405,11 +1494,11 @@ class NodeTypeTestCase extends DrupalWebTestCase {
$this->drupalPost('admin/structure/types/manage/bar', array(), t('Save content type'));
// Check that the body field doesn't exist.
$this->drupalGet('node/add/bar');
- $this->assertNoRaw('Body', t('Body field was not found.'));
+ $this->assertNoRaw('Body', 'Body field was not found.');
}
/**
- * Test that node_types_rebuild() correctly handles the 'disabled' flag.
+ * Tests that node_types_rebuild() correctly handles the 'disabled' flag.
*/
function testNodeTypeStatus() {
// Enable all core node modules, and all types should be active.
@@ -1417,42 +1506,42 @@ class NodeTypeTestCase extends DrupalWebTestCase {
node_types_rebuild();
$types = node_type_get_types();
foreach (array('blog', 'book', 'poll', 'article', 'page') as $type) {
- $this->assertTrue(isset($types[$type]), t('%type is found in node types.', array('%type' => $type)));
- $this->assertTrue(isset($types[$type]->disabled) && empty($types[$type]->disabled), t('%type type is enabled.', array('%type' => $type)));
+ $this->assertTrue(isset($types[$type]), format_string('%type is found in node types.', array('%type' => $type)));
+ $this->assertTrue(isset($types[$type]->disabled) && empty($types[$type]->disabled), format_string('%type type is enabled.', array('%type' => $type)));
}
// Disable poll module and the respective type should be marked as disabled.
module_disable(array('poll'), FALSE);
node_types_rebuild();
$types = node_type_get_types();
- $this->assertTrue(!empty($types['poll']->disabled), t("Poll module's node type disabled."));
- $this->assertTrue(isset($types['blog']) && empty($types['blog']->disabled), t("Blog module's node type still active."));
+ $this->assertTrue(!empty($types['poll']->disabled), "Poll module's node type disabled.");
+ $this->assertTrue(isset($types['blog']) && empty($types['blog']->disabled), "Blog module's node type still active.");
// Disable blog module and the respective type should be marked as disabled.
module_disable(array('blog'), FALSE);
node_types_rebuild();
$types = node_type_get_types();
- $this->assertTrue(!empty($types['blog']->disabled), t("Blog module's node type disabled."));
- $this->assertTrue(!empty($types['poll']->disabled), t("Poll module's node type still disabled."));
+ $this->assertTrue(!empty($types['blog']->disabled), "Blog module's node type disabled.");
+ $this->assertTrue(!empty($types['poll']->disabled), "Poll module's node type still disabled.");
// Disable book module and the respective type should still be active, since
// it is not provided by hook_node_info().
module_disable(array('book'), FALSE);
node_types_rebuild();
$types = node_type_get_types();
- $this->assertTrue(isset($types['book']) && empty($types['book']->disabled), t("Book module's node type still active."));
- $this->assertTrue(!empty($types['blog']->disabled), t("Blog module's node type still disabled."));
- $this->assertTrue(!empty($types['poll']->disabled), t("Poll module's node type still disabled."));
- $this->assertTrue(isset($types['article']) && empty($types['article']->disabled), t("Article node type still active."));
- $this->assertTrue(isset($types['page']) && empty($types['page']->disabled), t("Basic page node type still active."));
+ $this->assertTrue(isset($types['book']) && empty($types['book']->disabled), "Book module's node type still active.");
+ $this->assertTrue(!empty($types['blog']->disabled), "Blog module's node type still disabled.");
+ $this->assertTrue(!empty($types['poll']->disabled), "Poll module's node type still disabled.");
+ $this->assertTrue(isset($types['article']) && empty($types['article']->disabled), "Article node type still active.");
+ $this->assertTrue(isset($types['page']) && empty($types['page']->disabled), "Basic page node type still active.");
// Re-enable the modules and verify that the types are active again.
module_enable(array('blog', 'book', 'poll'), FALSE);
node_types_rebuild();
$types = node_type_get_types();
foreach (array('blog', 'book', 'poll', 'article', 'page') as $type) {
- $this->assertTrue(isset($types[$type]), t('%type is found in node types.', array('%type' => $type)));
- $this->assertTrue(isset($types[$type]->disabled) && empty($types[$type]->disabled), t('%type type is enabled.', array('%type' => $type)));
+ $this->assertTrue(isset($types[$type]), format_string('%type is found in node types.', array('%type' => $type)));
+ $this->assertTrue(isset($types[$type]->disabled) && empty($types[$type]->disabled), format_string('%type type is enabled.', array('%type' => $type)));
}
}
}
@@ -1470,7 +1559,7 @@ class NodeTypePersistenceTestCase extends DrupalWebTestCase {
}
/**
- * Test node type customizations persist through disable and uninstall.
+ * Tests that node type customizations persist through disable and uninstall.
*/
function testNodeTypeCustomizationPersistence() {
$web_user = $this->drupalCreateUser(array('bypass node access', 'administer content types', 'administer modules'));
@@ -1483,12 +1572,12 @@ class NodeTypePersistenceTestCase extends DrupalWebTestCase {
// disabled.
$this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
$disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
- $this->assertNotIdentical($disabled, FALSE, t('Poll node type found in the database'));
- $this->assertEqual($disabled, 0, t('Poll node type is not disabled'));
+ $this->assertNotIdentical($disabled, FALSE, 'Poll node type found in the database');
+ $this->assertEqual($disabled, 0, 'Poll node type is not disabled');
// Check that poll node type (uncustomized) shows up.
$this->drupalGet('node/add');
- $this->assertText('poll', t('poll type is found on node/add'));
+ $this->assertText('poll', 'poll type is found on node/add');
// Customize poll description.
$description = $this->randomName();
@@ -1497,23 +1586,23 @@ class NodeTypePersistenceTestCase extends DrupalWebTestCase {
// Check that poll node type customization shows up.
$this->drupalGet('node/add');
- $this->assertText($description, t('Customized description found'));
+ $this->assertText($description, 'Customized description found');
// Disable poll and check that the node type gets disabled.
$this->drupalPost('admin/modules', $poll_disable, t('Save configuration'));
$disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
- $this->assertEqual($disabled, 1, t('Poll node type is disabled'));
+ $this->assertEqual($disabled, 1, 'Poll node type is disabled');
$this->drupalGet('node/add');
- $this->assertNoText('poll', t('poll type is not found on node/add'));
+ $this->assertNoText('poll', 'poll type is not found on node/add');
// Reenable poll and check that the customization survived the module
// disable.
$this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
$disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
- $this->assertNotIdentical($disabled, FALSE, t('Poll node type found in the database'));
- $this->assertEqual($disabled, 0, t('Poll node type is not disabled'));
+ $this->assertNotIdentical($disabled, FALSE, 'Poll node type found in the database');
+ $this->assertEqual($disabled, 0, 'Poll node type is not disabled');
$this->drupalGet('node/add');
- $this->assertText($description, t('Customized description found'));
+ $this->assertText($description, 'Customized description found');
// Disable and uninstall poll.
$this->drupalPost('admin/modules', $poll_disable, t('Save configuration'));
@@ -1521,20 +1610,20 @@ class NodeTypePersistenceTestCase extends DrupalWebTestCase {
$this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
$this->drupalPost(NULL, array(), t('Uninstall'));
$disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
- $this->assertTrue($disabled, t('Poll node type is in the database and is disabled'));
+ $this->assertTrue($disabled, 'Poll node type is in the database and is disabled');
$this->drupalGet('node/add');
- $this->assertNoText('poll', t('poll type is no longer found on node/add'));
+ $this->assertNoText('poll', 'poll type is no longer found on node/add');
// Reenable poll and check that the customization survived the module
// uninstall.
$this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
$this->drupalGet('node/add');
- $this->assertText($description, t('Customized description is found even after uninstall and reenable.'));
+ $this->assertText($description, 'Customized description is found even after uninstall and reenable.');
}
}
/**
- * Rebuild the node_access table.
+ * Verifies the rebuild functionality for the node_access table.
*/
class NodeAccessRebuildTestCase extends DrupalWebTestCase {
public static function getInfo() {
@@ -1553,6 +1642,9 @@ class NodeAccessRebuildTestCase extends DrupalWebTestCase {
$this->web_user = $web_user;
}
+ /**
+ * Tests rebuilding the node access permissions table.
+ */
function testNodeAccessRebuild() {
$this->drupalGet('admin/reports/status');
$this->clickLink(t('Rebuild permissions'));
@@ -1562,7 +1654,7 @@ class NodeAccessRebuildTestCase extends DrupalWebTestCase {
}
/**
- * Test node administration page functionality.
+ * Tests node administration page functionality.
*/
class NodeAdminTestCase extends DrupalWebTestCase {
public static function getInfo() {
@@ -1630,6 +1722,7 @@ class NodeAdminTestCase extends DrupalWebTestCase {
* Tests content overview with different user permissions.
*
* Taxonomy filters are tested separately.
+ *
* @see TaxonomyNodeFilterTestCase
*/
function testContentAdminPages() {
@@ -1648,7 +1741,7 @@ class NodeAdminTestCase extends DrupalWebTestCase {
$this->assertLinkByHref('node/' . $node->nid . '/edit');
$this->assertLinkByHref('node/' . $node->nid . '/delete');
// Verify tableselect.
- $this->assertFieldByName('nodes[' . $node->nid . ']', '', t('Tableselect found.'));
+ $this->assertFieldByName('nodes[' . $node->nid . ']', '', 'Tableselect found.');
}
// Verify filtering by publishing status.
@@ -1657,7 +1750,7 @@ class NodeAdminTestCase extends DrupalWebTestCase {
);
$this->drupalPost(NULL, $edit, t('Filter'));
- $this->assertRaw(t('where %property is %value', array('%property' => t('status'), '%value' => 'published')), t('Content list is filtered by status.'));
+ $this->assertRaw(t('where %property is %value', array('%property' => t('status'), '%value' => 'published')), 'Content list is filtered by status.');
$this->assertLinkByHref('node/' . $nodes['published_page']->nid . '/edit');
$this->assertLinkByHref('node/' . $nodes['published_article']->nid . '/edit');
@@ -1669,8 +1762,8 @@ class NodeAdminTestCase extends DrupalWebTestCase {
);
$this->drupalPost(NULL, $edit, t('Refine'));
- $this->assertRaw(t('where %property is %value', array('%property' => t('status'), '%value' => 'published')), t('Content list is filtered by status.'));
- $this->assertRaw(t('and where %property is %value', array('%property' => t('type'), '%value' => 'Basic page')), t('Content list is filtered by content type.'));
+ $this->assertRaw(t('where %property is %value', array('%property' => t('status'), '%value' => 'published')), 'Content list is filtered by status.');
+ $this->assertRaw(t('and where %property is %value', array('%property' => t('type'), '%value' => 'Basic page')), 'Content list is filtered by content type.');
$this->assertLinkByHref('node/' . $nodes['published_page']->nid . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['published_article']->nid . '/edit');
@@ -1693,7 +1786,7 @@ class NodeAdminTestCase extends DrupalWebTestCase {
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->nid . '/delete');
// Verify no tableselect.
- $this->assertNoFieldByName('nodes[' . $nodes['published_page']->nid . ']', '', t('No tableselect found.'));
+ $this->assertNoFieldByName('nodes[' . $nodes['published_page']->nid . ']', '', 'No tableselect found.');
// Verify unpublished content is displayed with permission.
$this->drupalLogout();
@@ -1711,7 +1804,7 @@ class NodeAdminTestCase extends DrupalWebTestCase {
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->nid . '/delete');
// Verify no tableselect.
- $this->assertNoFieldByName('nodes[' . $nodes['unpublished_page_2']->nid . ']', '', t('No tableselect found.'));
+ $this->assertNoFieldByName('nodes[' . $nodes['unpublished_page_2']->nid . ']', '', 'No tableselect found.');
// Verify node access can be bypassed.
$this->drupalLogout();
@@ -1727,9 +1820,15 @@ class NodeAdminTestCase extends DrupalWebTestCase {
}
/**
- * Test node title.
+ * Tests node title functionality.
*/
class NodeTitleTestCase extends DrupalWebTestCase {
+
+ /**
+ * A user with permission to create and edit content and to administer nodes.
+ *
+ * @var object
+ */
protected $admin_user;
public static function getInfo() {
@@ -1747,7 +1846,7 @@ class NodeTitleTestCase extends DrupalWebTestCase {
}
/**
- * Create one node and test if the node title has the correct value.
+ * Creates one node and tests if the node title has the correct value.
*/
function testNodeTitle() {
// Create "Basic page" content with title.
@@ -1790,7 +1889,7 @@ class NodeFeedTestCase extends DrupalWebTestCase {
}
/**
- * Ensure that node_feed accepts and prints extra channel elements.
+ * Ensures that node_feed() accepts and prints extra channel elements.
*/
function testNodeFeedExtraChannelElements() {
ob_start();
@@ -1822,7 +1921,7 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase {
}
/**
- * Test the recent comments block.
+ * Tests the recent comments block.
*/
function testRecentNodeBlock() {
$this->drupalLogin($this->admin_user);
@@ -1837,7 +1936,7 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase {
'blocks[node_recent][region]' => 'sidebar_first',
);
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
- $this->assertText(t('The block settings have been updated.'), t('Block saved to first sidebar region.'));
+ $this->assertText(t('The block settings have been updated.'), 'Block saved to first sidebar region.');
// Set block title and variables.
$block = array(
@@ -1845,11 +1944,11 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase {
'node_recent_block_count' => 2,
);
$this->drupalPost('admin/structure/block/manage/node/recent/configure', $block, t('Save block'));
- $this->assertText(t('The block configuration has been saved.'), t('Block saved.'));
+ $this->assertText(t('The block configuration has been saved.'), 'Block saved.');
// Test that block is not visible without nodes
$this->drupalGet('');
- $this->assertText(t('No content available.'), t('Block with "No content available." found.'));
+ $this->assertText(t('No content available.'), 'Block with "No content available." found.');
// Add some test nodes.
$default_settings = array('uid' => $this->web_user->uid, 'type' => 'article');
@@ -1875,16 +1974,16 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase {
// see the block.
$this->drupalLogout();
$this->drupalGet('');
- $this->assertNoText($block['title'], t('Block was not found.'));
+ $this->assertNoText($block['title'], 'Block was not found.');
// Test that only the 2 latest nodes are shown.
$this->drupalLogin($this->web_user);
- $this->assertNoText($node1->title, t('Node not found in block.'));
- $this->assertText($node2->title, t('Node found in block.'));
- $this->assertText($node3->title, t('Node found in block.'));
+ $this->assertNoText($node1->title, 'Node not found in block.');
+ $this->assertText($node2->title, 'Node found in block.');
+ $this->assertText($node3->title, 'Node found in block.');
// Check to make sure nodes are in the right order.
- $this->assertTrue($this->xpath('//div[@id="block-node-recent"]/div/table/tbody/tr[position() = 1]/td/div/a[text() = "' . $node3->title . '"]'), t('Nodes were ordered correctly in block.'));
+ $this->assertTrue($this->xpath('//div[@id="block-node-recent"]/div/table/tbody/tr[position() = 1]/td/div/a[text() = "' . $node3->title . '"]'), 'Nodes were ordered correctly in block.');
// Set the number of recent nodes to show to 10.
$this->drupalLogout();
@@ -1893,17 +1992,17 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase {
'node_recent_block_count' => 10,
);
$this->drupalPost('admin/structure/block/manage/node/recent/configure', $block, t('Save block'));
- $this->assertText(t('The block configuration has been saved.'), t('Block saved.'));
+ $this->assertText(t('The block configuration has been saved.'), 'Block saved.');
// Post an additional node.
$node4 = $this->drupalCreateNode($default_settings);
// Test that all four nodes are shown.
$this->drupalGet('');
- $this->assertText($node1->title, t('Node found in block.'));
- $this->assertText($node2->title, t('Node found in block.'));
- $this->assertText($node3->title, t('Node found in block.'));
- $this->assertText($node4->title, t('Node found in block.'));
+ $this->assertText($node1->title, 'Node found in block.');
+ $this->assertText($node2->title, 'Node found in block.');
+ $this->assertText($node3->title, 'Node found in block.');
+ $this->assertText($node4->title, 'Node found in block.');
// Create the custom block.
$custom_block = array();
@@ -1918,24 +2017,24 @@ class NodeBlockFunctionalTest extends DrupalWebTestCase {
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
- $this->assertTrue($bid, t('Custom block with visibility rule was created.'));
+ $this->assertTrue($bid, 'Custom block with visibility rule was created.');
// Verify visibility rules.
$this->drupalGet('');
- $this->assertNoText($custom_block['title'], t('Block was displayed on the front page.'));
+ $this->assertNoText($custom_block['title'], 'Block was displayed on the front page.');
$this->drupalGet('node/add/article');
- $this->assertText($custom_block['title'], t('Block was displayed on the node/add/article page.'));
+ $this->assertText($custom_block['title'], 'Block was displayed on the node/add/article page.');
$this->drupalGet('node/' . $node1->nid);
- $this->assertText($custom_block['title'], t('Block was displayed on the node/N.'));
+ $this->assertText($custom_block['title'], 'Block was displayed on the node/N.');
// Delete the created custom block & verify that it's been deleted.
$this->drupalPost('admin/structure/block/manage/block/' . $bid . '/delete', array(), t('Delete'));
$bid = db_query("SELECT 1 FROM {block_node_type} WHERE module = 'block' AND delta = :delta", array(':delta' => $bid))->fetchField();
- $this->assertFalse($bid, t('Custom block was deleted.'));
+ $this->assertFalse($bid, 'Custom block was deleted.');
}
}
/**
- * Test multistep node forms basic options.
+ * Tests basic options of multi-step node forms.
*/
class MultiStepNodeFormBasicOptionsTest extends DrupalWebTestCase {
public static function getInfo() {
@@ -1953,7 +2052,7 @@ class MultiStepNodeFormBasicOptionsTest extends DrupalWebTestCase {
}
/**
- * Change the default values of basic options to ensure they persist.
+ * Tests changing the default values of basic options to ensure they persist.
*/
function testMultiStepNodeFormBasicOptions() {
$edit = array(
@@ -1985,7 +2084,7 @@ class NodeBuildContent extends DrupalWebTestCase {
}
/**
- * Test to ensure that a node's content array is rebuilt on every call to node_build_content().
+ * Ensures that content array is rebuilt on every call to node_build_content().
*/
function testNodeRebuildContent() {
$node = $this->drupalCreateNode();
@@ -1995,7 +2094,7 @@ class NodeBuildContent extends DrupalWebTestCase {
$content = node_build_content($node);
// If the property doesn't exist it means the node->content was rebuilt.
- $this->assertFalse(isset($content['test_content_property']), t('Node content was emptied prior to being built.'));
+ $this->assertFalse(isset($content['test_content_property']), 'Node content was emptied prior to being built.');
}
}
@@ -2014,11 +2113,15 @@ class NodeQueryAlter extends DrupalWebTestCase {
/**
* User with permission to view content.
+ *
+ * @var object
*/
protected $accessUser;
/**
* User without permission to view content.
+ *
+ * @var object
*/
protected $noAccessUser;
@@ -2065,10 +2168,10 @@ class NodeQueryAlter extends DrupalWebTestCase {
}
/**
- * Lower-level test of 'node_access' query alter, for user with access.
+ * Tests 'node_access' query alter, for user with access.
*
- * Verifies that a non-standard table alias can be used, and that a
- * user with node access can view the nodes.
+ * Verifies that a non-standard table alias can be used, and that a user with
+ * node access can view the nodes.
*/
function testNodeQueryAlterLowLevelWithAccess() {
// User with access should be able to view 4 nodes.
@@ -2080,7 +2183,7 @@ class NodeQueryAlter extends DrupalWebTestCase {
$query->addMetaData('account', $this->accessUser);
$result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 4, t('User with access can see correct nodes'));
+ $this->assertEqual(count($result), 4, 'User with access can see correct nodes');
}
catch (Exception $e) {
$this->fail(t('Altered query is malformed'));
@@ -2088,10 +2191,10 @@ class NodeQueryAlter extends DrupalWebTestCase {
}
/**
- * Lower-level test of 'node_access' query alter, for user without access.
+ * Tests 'node_access' query alter, for user without access.
*
- * Verifies that a non-standard table alias can be used, and that a
- * user without node access cannot view the nodes.
+ * Verifies that a non-standard table alias can be used, and that a user
+ * without node access cannot view the nodes.
*/
function testNodeQueryAlterLowLevelNoAccess() {
// User without access should be able to view 0 nodes.
@@ -2103,7 +2206,7 @@ class NodeQueryAlter extends DrupalWebTestCase {
$query->addMetaData('account', $this->noAccessUser);
$result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 0, t('User with no access cannot see nodes'));
+ $this->assertEqual(count($result), 0, 'User with no access cannot see nodes');
}
catch (Exception $e) {
$this->fail(t('Altered query is malformed'));
@@ -2111,10 +2214,10 @@ class NodeQueryAlter extends DrupalWebTestCase {
}
/**
- * Lower-level test of 'node_access' query alter, for edit access.
+ * Tests 'node_access' query alter, for edit access.
*
- * Verifies that a non-standard table alias can be used, and that a
- * user with view-only node access cannot edit the nodes.
+ * Verifies that a non-standard table alias can be used, and that a user with
+ * view-only node access cannot edit the nodes.
*/
function testNodeQueryAlterLowLevelEditAccess() {
// User with view-only access should not be able to edit nodes.
@@ -2126,7 +2229,7 @@ class NodeQueryAlter extends DrupalWebTestCase {
$query->addMetaData('account', $this->accessUser);
$result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 0, t('User with view-only access cannot edit nodes'));
+ $this->assertEqual(count($result), 0, 'User with view-only access cannot edit nodes');
}
catch (Exception $e) {
$this->fail($e->getMessage());
@@ -2136,13 +2239,13 @@ class NodeQueryAlter extends DrupalWebTestCase {
}
/**
- * Lower-level test of 'node_access' query alter override.
+ * Tests 'node_access' query alter override.
*
* Verifies that node_access_view_all_nodes() is called from
- * node_query_node_access_alter(). We do this by checking that
- * a user which normally would not have view privileges is able
- * to view the nodes when we add a record to {node_access} paired
- * with a corresponding privilege in hook_node_grants().
+ * node_query_node_access_alter(). We do this by checking that a user who
+ * normally would not have view privileges is able to view the nodes when we
+ * add a record to {node_access} paired with a corresponding privilege in
+ * hook_node_grants().
*/
function testNodeQueryAlterOverride() {
$record = array(
@@ -2166,7 +2269,7 @@ class NodeQueryAlter extends DrupalWebTestCase {
$query->addMetaData('account', $this->noAccessUser);
$result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 0, t('User view privileges are not overridden'));
+ $this->assertEqual(count($result), 0, 'User view privileges are not overridden');
}
catch (Exception $e) {
$this->fail(t('Altered query is malformed'));
@@ -2188,7 +2291,7 @@ class NodeQueryAlter extends DrupalWebTestCase {
$query->addMetaData('account', $this->noAccessUser);
$result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 4, t('User view privileges are overridden'));
+ $this->assertEqual(count($result), 4, 'User view privileges are overridden');
}
catch (Exception $e) {
$this->fail(t('Altered query is malformed'));
@@ -2213,11 +2316,15 @@ class NodeEntityFieldQueryAlter extends DrupalWebTestCase {
/**
* User with permission to view content.
+ *
+ * @var object
*/
protected $accessUser;
/**
* User without permission to view content.
+ *
+ * @var object
*/
protected $noAccessUser;
@@ -2319,11 +2426,11 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
$tests['[node:changed:since]'] = format_interval(REQUEST_TIME - $node->changed, 2, $language->language);
// 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('node' => $node), array('language' => $language));
- $this->assertEqual($output, $expected, t('Sanitized node token %token replaced.', array('%token' => $input)));
+ $this->assertEqual($output, $expected, format_string('Sanitized node token %token replaced.', array('%token' => $input)));
}
// Generate and test unsanitized tokens.
@@ -2335,7 +2442,36 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
foreach ($tests as $input => $expected) {
$output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
- $this->assertEqual($output, $expected, t('Unsanitized node token %token replaced.', array('%token' => $input)));
+ $this->assertEqual($output, $expected, format_string('Unsanitized node token %token replaced.', array('%token' => $input)));
+ }
+
+ // Repeat for a node without a summary.
+ $settings['body'] = array(LANGUAGE_NONE => array(array('value' => $this->randomName(32), 'summary' => '')));
+ $node = $this->drupalCreateNode($settings);
+
+ // Load node (without summary) so that the body and summary fields are
+ // structured properly.
+ $node = node_load($node->nid);
+ $instance = field_info_instance('node', 'body', $node->type);
+
+ // Generate and test sanitized token - use full body as expected value.
+ $tests = array();
+ $tests['[node:summary]'] = _text_sanitize($instance, $langcode, $node->body[$langcode][0], 'value');
+
+ // Test to make sure that we generated something for each token.
+ $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated for node without a summary.');
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('node' => $node), array('language' => $language));
+ $this->assertEqual($output, $expected, format_string('Sanitized node token %token replaced for node without a summary.', array('%token' => $input)));
+ }
+
+ // Generate and test unsanitized tokens.
+ $tests['[node:summary]'] = $node->body[$langcode][0]['value'];
+
+ foreach ($tests as $input => $expected) {
+ $output = token_replace($input, array('node' => $node), array('language' => $language, 'sanitize' => FALSE));
+ $this->assertEqual($output, $expected, format_string('Unsanitized node token %token replaced for node without a summary.', array('%token' => $input)));
}
}
}
@@ -2344,10 +2480,26 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase {
* Tests user permissions for node revisions.
*/
class NodeRevisionPermissionsTestCase extends DrupalWebTestCase {
+
+ /**
+ * Nodes used by the test.
+ *
+ * @var array
+ */
protected $node_revisions = array();
+
+ /**
+ * Users with different revision permission used by the test.
+ *
+ * @var array
+ */
protected $accounts = array();
- // Map revision permission names to node revision access ops.
+ /**
+ * Map revision permission names to node revision access ops.
+ *
+ * @var array
+ */
protected $map = array(
'view' => 'view revisions',
'update' => 'revert revisions',
@@ -2630,8 +2782,8 @@ class NodeEntityViewModeAlterTest extends NodeWebTestCase {
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $this->randomName(8);
- $edit["body[$langcode][0][value]"] = t('Data that should appear only in the body for the node.');
- $edit["body[$langcode][0][summary]"] = t('Extra data that should appear only in the teaser for the node.');
+ $edit["body[$langcode][0][value]"] = 'Data that should appear only in the body for the node.';
+ $edit["body[$langcode][0][summary]"] = 'Extra data that should appear only in the teaser for the node.';
$this->drupalPost('node/add/page', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit["title"]);
@@ -2649,4 +2801,118 @@ class NodeEntityViewModeAlterTest extends NodeWebTestCase {
$build = node_view($node);
$this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
}
+
+ /**
+ * Tests fields that were previously hidden when the view mode is changed.
+ */
+ function testNodeViewModeChangeHiddenField() {
+ // Hide the tags field on the default display
+ $instance = field_info_instance('node', 'field_tags', 'article');
+ $instance['display']['default']['type'] = 'hidden';
+ field_update_instance($instance);
+
+ $web_user = $this->drupalCreateUser(array('create article content', 'edit own article content'));
+ $this->drupalLogin($web_user);
+
+ // Create a node.
+ $edit = array();
+ $langcode = LANGUAGE_NONE;
+ $edit["title"] = $this->randomName(8);
+ $edit["body[$langcode][0][value]"] = 'Data that should appear only in the body for the node.';
+ $edit["body[$langcode][0][summary]"] = 'Extra data that should appear only in the teaser for the node.';
+ $edit["field_tags[$langcode]"] = 'Extra tag';
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+
+ $node = $this->drupalGetNodeByTitle($edit["title"]);
+
+ // Set the flag to alter the view mode and view the node.
+ variable_set('node_test_change_view_mode', 'teaser');
+ $this->drupalGet('node/' . $node->nid);
+
+ // Check that teaser mode is viewed.
+ $this->assertText('Extra data that should appear only in the teaser for the node.', 'Teaser text present');
+ // Make sure body text is not present.
+ $this->assertNoText('Data that should appear only in the body for the node.', 'Body text not present');
+ // Make sure tags are present.
+ $this->assertText('Extra tag', 'Taxonomy term present');
+
+ // Test that the correct build mode has been set.
+ $build = node_view($node);
+ $this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
+ }
+}
+
+/**
+ * Tests the cache invalidation of node operations.
+ */
+class NodePageCacheTest extends NodeWebTestCase {
+
+ /**
+ * An admin user with administrative permissions for nodes.
+ */
+ protected $admin_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Node page cache test',
+ 'description' => 'Test cache invalidation of node operations.',
+ 'group' => 'Node',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ variable_set('cache', 1);
+ variable_set('page_cache_maximum_age', 300);
+
+ $this->admin_user = $this->drupalCreateUser(array(
+ 'bypass node access',
+ 'access content overview',
+ 'administer nodes',
+ ));
+ }
+
+ /**
+ * Tests deleting nodes clears page cache.
+ */
+ public function testNodeDelete() {
+ $node_path = 'node/' . $this->drupalCreateNode()->nid;
+
+ // Populate page cache.
+ $this->drupalGet($node_path);
+
+ // Login and delete the node.
+ $this->drupalLogin($this->admin_user);
+ $this->drupalPost($node_path . '/delete', array(), t('Delete'));
+
+ // Logout and check the node is not available.
+ $this->drupalLogout();
+ $this->drupalGet($node_path);
+ $this->assertResponse(404);
+
+ // Create two new nodes.
+ $nodes[0] = $this->drupalCreateNode();
+ $nodes[1] = $this->drupalCreateNode();
+ $node_path = 'node/' . $nodes[0]->nid;
+
+ // Populate page cache.
+ $this->drupalGet($node_path);
+
+ // Login and delete the nodes.
+ $this->drupalLogin($this->admin_user);
+ $this->drupalGet('admin/content');
+ $edit = array(
+ 'operation' => 'delete',
+ 'nodes[' . $nodes[0]->nid . ']' => TRUE,
+ 'nodes[' . $nodes[1]->nid . ']' => TRUE,
+ );
+ $this->drupalPost(NULL, $edit, t('Update'));
+ $this->drupalPost(NULL, array(), t('Delete'));
+
+ // Logout and check the node is not available.
+ $this->drupalLogout();
+ $this->drupalGet($node_path);
+ $this->assertResponse(404);
+ }
}
diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc
index e43db5e..e63c751 100644
--- a/modules/node/node.tokens.inc
+++ b/modules/node/node.tokens.inc
@@ -136,10 +136,29 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr
case 'body':
case 'summary':
if ($items = field_get_items('node', $node, 'body', $language_code)) {
- $column = ($name == 'body') ? 'value' : 'summary';
$instance = field_info_instance('node', 'body', $node->type);
$field_langcode = field_language('node', $node, 'body', $language_code);
- $replacements[$original] = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], $column) : $items[0][$column];
+ // If the summary was requested and is not empty, use it.
+ if ($name == 'summary' && !empty($items[0]['summary'])) {
+ $output = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'summary') : $items[0]['summary'];
+ }
+ // Attempt to provide a suitable version of the 'body' field.
+ else {
+ $output = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'value') : $items[0]['value'];
+ // A summary was requested.
+ if ($name == 'summary') {
+ if (isset($instance['display']['teaser']['settings']['trim_length'])) {
+ $trim_length = $instance['display']['teaser']['settings']['trim_length'];
+ }
+ else {
+ // Use default value.
+ $trim_length = NULL;
+ }
+ // Generate an optionally trimmed summary of the body field.
+ $output = text_summary($output, $instance['settings']['text_processing'] ? $items[0]['format'] : NULL, $trim_length);
+ }
+ }
+ $replacements[$original] = $output;
}
break;
diff --git a/modules/node/tests/node_access_test.info b/modules/node/tests/node_access_test.info
index dc5e530..69362c9 100644
--- a/modules/node/tests/node_access_test.info
+++ b/modules/node/tests/node_access_test.info
@@ -4,3 +4,9 @@ package = Testing
version = VERSION
core = 7.x
hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/node/tests/node_access_test.module b/modules/node/tests/node_access_test.module
index 813bf92..7932c55 100644
--- a/modules/node/tests/node_access_test.module
+++ b/modules/node/tests/node_access_test.module
@@ -2,7 +2,9 @@
/**
* @file
- * Dummy module implementing node access related hooks to test API interaction
+ * A dummy module implementing node access related hooks for testing purposes.
+ *
+ * A dummy module implementing node access related hooks to test API interaction
* with the Node module. This module restricts view permission to those with
* a special 'node test view' permission.
*/
@@ -140,6 +142,8 @@ function node_access_test_page() {
* database query is shown, and a list of the node IDs, for debugging purposes.
* And if there is a query exception, the page says "Exception" and gives the
* error.
+ *
+ * @see node_access_test_menu()
*/
function node_access_entity_test_page() {
$output = '';
@@ -207,7 +211,7 @@ function node_access_test_node_insert($node) {
}
/**
- * Implements hook_nodeapi_update().
+ * Implements hook_node_update().
*/
function node_access_test_node_update($node) {
_node_access_test_node_write($node);
diff --git a/modules/node/tests/node_test.info b/modules/node/tests/node_test.info
index fa6c093..aa658ea 100644
--- a/modules/node/tests/node_test.info
+++ b/modules/node/tests/node_test.info
@@ -4,3 +4,9 @@ package = Testing
version = VERSION
core = 7.x
hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/node/tests/node_test.module b/modules/node/tests/node_test.module
index a52c1fa..edc175f 100644
--- a/modules/node/tests/node_test.module
+++ b/modules/node/tests/node_test.module
@@ -2,8 +2,10 @@
/**
* @file
- * Dummy module implementing node related hooks to test API interaction with
- * the Node module.
+ * A dummy module for testing node related hooks.
+ *
+ * This is a dummy module that implements node related hooks to test API
+ * interaction with the Node module.
*/
/**
@@ -159,3 +161,21 @@ function node_test_entity_view_mode_alter(&$view_mode, $context) {
$view_mode = $change_view_mode;
}
}
+
+/**
+ * Implements hook_node_insert().
+ *
+ * This tests saving a node on node insert.
+ *
+ * @see NodeSaveTest::testNodeSaveOnInsert()
+ */
+function node_test_node_insert($node) {
+ // Set the node title to the node ID and save.
+ if ($node->title == 'new') {
+ $node->title = 'Node '. $node->nid;
+ // Remove the is_new flag, so that the node is updated and not inserted
+ // again.
+ unset($node->is_new);
+ node_save($node);
+ }
+}
diff --git a/modules/node/tests/node_test_exception.info b/modules/node/tests/node_test_exception.info
index 1a339e0..4289481 100644
--- a/modules/node/tests/node_test_exception.info
+++ b/modules/node/tests/node_test_exception.info
@@ -4,3 +4,9 @@ package = Testing
version = VERSION
core = 7.x
hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/node/tests/node_test_exception.module b/modules/node/tests/node_test_exception.module
index 0fe9f35..66bc717 100644
--- a/modules/node/tests/node_test_exception.module
+++ b/modules/node/tests/node_test_exception.module
@@ -2,8 +2,7 @@
/**
* @file
- * Dummy module implementing node related hooks to test API interaction with
- * the Node module.
+ * A module implementing node related hooks to test API interaction.
*/
/**
diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc
index 74a08d5..a1da1d0 100644
--- a/modules/openid/openid.inc
+++ b/modules/openid/openid.inc
@@ -158,6 +158,11 @@ function _openid_xrds_parse($raw_xml) {
return array();
}
+ // Also stop parsing if there is an unreasonably large number of tags.
+ if ($dom->getElementsByTagName('*')->length > variable_get('openid_xrds_maximum_tag_count', 30000)) {
+ return array();
+ }
+
// Parse the DOM document for the information we need.
if ($xml = simplexml_import_dom($dom)) {
foreach ($xml->children(OPENID_NS_XRD)->XRD as $xrd) {
@@ -380,6 +385,9 @@ function _openid_parse_message($message) {
/**
* Return a nonce value - formatted per OpenID spec.
+ *
+ * NOTE: This nonce is not cryptographically secure and only suitable for use
+ * by the test framework.
*/
function _openid_nonce() {
// YYYY-MM-DDThh:mm:ssZ, plus some optional extra unique characters.
@@ -549,7 +557,7 @@ function _openid_dh_rand($stop) {
}
do {
- $bytes = "\x00" . _openid_get_bytes($nbytes);
+ $bytes = "\x00" . drupal_random_bytes($nbytes);
$n = _openid_dh_binary_to_long($bytes);
// Keep looping if this value is in the low duplicated range.
} while (_openid_math_cmp($n, $duplicate) < 0);
@@ -558,23 +566,7 @@ function _openid_dh_rand($stop) {
}
function _openid_get_bytes($num_bytes) {
- $f = &drupal_static(__FUNCTION__);
- $bytes = '';
- if (!isset($f)) {
- $f = @fopen(OPENID_RAND_SOURCE, "r");
- }
- if (!$f) {
- // pseudorandom used
- $bytes = '';
- for ($i = 0; $i < $num_bytes; $i += 4) {
- $bytes .= pack('L', mt_rand());
- }
- $bytes = substr($bytes, 0, $num_bytes);
- }
- else {
- $bytes = fread($f, $num_bytes);
- }
- return $bytes;
+ return drupal_random_bytes($num_bytes);
}
function _openid_response($str = NULL) {
diff --git a/modules/openid/openid.info b/modules/openid/openid.info
index 6a1843c..3acd5dc 100644
--- a/modules/openid/openid.info
+++ b/modules/openid/openid.info
@@ -4,3 +4,9 @@ version = VERSION
package = Core
core = 7.x
files[] = openid.test
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/openid/openid.install b/modules/openid/openid.install
index 4b77b71..e382d86 100644
--- a/modules/openid/openid.install
+++ b/modules/openid/openid.install
@@ -15,13 +15,14 @@ function openid_schema() {
'idp_endpoint_uri' => array(
'type' => 'varchar',
'length' => 255,
- 'description' => 'URI of the OpenID Provider endpoint.',
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
),
'assoc_handle' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
- 'description' => 'Primary Key: Used to refer to this association in subsequent messages.',
+ 'description' => 'Used to refer to this association in subsequent messages.',
),
'assoc_type' => array(
'type' => 'varchar',
@@ -51,7 +52,10 @@ function openid_schema() {
'description' => 'The lifetime, in seconds, of this association.',
),
),
- 'primary key' => array('assoc_handle'),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
);
$schema['openid_nonce'] = array(
@@ -158,3 +162,69 @@ function openid_update_6000() {
/**
* @} End of "addtogroup updates-6.x-to-7.x".
*/
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Bind associations to their providers.
+ */
+function openid_update_7000() {
+ db_drop_table('openid_association');
+
+ $schema = array(
+ 'description' => 'Stores temporary shared key association information for OpenID authentication.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
+ ),
+ 'assoc_handle' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Used to refer to this association in subsequent messages.',
+ ),
+ 'assoc_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'The signature algorithm used: one of HMAC-SHA1 or HMAC-SHA256.',
+ ),
+ 'session_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'Valid association session types: "no-encryption", "DH-SHA1", and "DH-SHA256".',
+ ),
+ 'mac_key' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The MAC key (shared secret) for this association.',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'UNIX timestamp for when the association was created.',
+ ),
+ 'expires_in' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The lifetime, in seconds, of this association.',
+ ),
+ ),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
+ );
+ db_create_table('openid_association', $schema);
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ */
diff --git a/modules/openid/openid.module b/modules/openid/openid.module
index a3f4fc8..a28f452 100644
--- a/modules/openid/openid.module
+++ b/modules/openid/openid.module
@@ -787,7 +787,21 @@ function openid_authentication_request($claimed_id, $identity, $return_to = '',
$request = array_merge($request, module_invoke_all('openid', 'request', $request));
- return $request;
+ // module_invoke_all() uses array_merge_recursive() which might return nested
+ // arrays if two or more modules alter a given parameter, resulting in an
+ // invalid request format. To ensure this doesn't happen, we flatten the returned
+ // value by taking the last entry in the array if an array is returned.
+ $flattened_request = array();
+ foreach ($request as $key => $value) {
+ if (is_array($value)) {
+ $flattened_request[$key] = end($value);
+ }
+ else {
+ $flattened_request[$key] = $value;
+ }
+ }
+
+ return $flattened_request;
}
/**
@@ -825,7 +839,7 @@ function openid_verify_assertion($service, $response) {
// direct verification: ignore the openid.assoc_handle, even if present.
// See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.1
if (!empty($response['openid.assoc_handle']) && empty($response['openid.invalidate_handle'])) {
- $association = db_query("SELECT * FROM {openid_association} WHERE assoc_handle = :assoc_handle", array(':assoc_handle' => $response['openid.assoc_handle']))->fetchObject();
+ $association = db_query("SELECT * FROM {openid_association} WHERE idp_endpoint_uri = :endpoint AND assoc_handle = :assoc_handle", array(':endpoint' => $service['uri'], ':assoc_handle' => $response['openid.assoc_handle']))->fetchObject();
}
if ($association && isset($association->session_type)) {
@@ -857,6 +871,7 @@ function openid_verify_assertion($service, $response) {
// database to avoid reusing it again on a subsequent authentication request.
// See http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2.2
db_delete('openid_association')
+ ->condition('idp_endpoint_uri', $service['uri'])
->condition('assoc_handle', $response['invalidate_handle'])
->execute();
}
diff --git a/modules/openid/openid.test b/modules/openid/openid.test
index 292c531..41af3f8 100644
--- a/modules/openid/openid.test
+++ b/modules/openid/openid.test
@@ -694,13 +694,6 @@ class OpenIDTestCase extends DrupalWebTestCase {
$this->assertEqual(_openid_dh_xorsecret('123456790123456790123456790', "abc123ABC\x00\xFF"), "\xa4'\x06\xbe\xf1.\x00y\xff\xc2\xc1", '_openid_dh_xorsecret() returned expected result.');
}
- /**
- * Test _openid_get_bytes().
- */
- function testOpenidGetBytes() {
- $this->assertEqual(strlen(_openid_get_bytes(20)), 20, '_openid_get_bytes() returned expected result.');
- }
-
/**
* Test _openid_signature().
*/
diff --git a/modules/openid/tests/openid_test.info b/modules/openid/tests/openid_test.info
index 755a5ee..c828275 100644
--- a/modules/openid/tests/openid_test.info
+++ b/modules/openid/tests/openid_test.info
@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
dependencies[] = openid
hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/openid/tests/openid_test.install b/modules/openid/tests/openid_test.install
index 3bd4978..d30e2dc 100644
--- a/modules/openid/tests/openid_test.install
+++ b/modules/openid/tests/openid_test.install
@@ -13,5 +13,5 @@ function openid_test_install() {
// Generate a MAC key (Message Authentication Code) used for signing messages.
// The variable is base64-encoded, because variables cannot contain non-UTF-8
// data.
- variable_set('openid_test_mac_key', base64_encode(_openid_get_bytes(20)));
+ variable_set('openid_test_mac_key', drupal_random_key(20));
}
diff --git a/modules/overlay/overlay-parent.js b/modules/overlay/overlay-parent.js
index 4134759..7452a51 100644
--- a/modules/overlay/overlay-parent.js
+++ b/modules/overlay/overlay-parent.js
@@ -630,8 +630,11 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) {
$target.attr('href', $.param.querystring(href, { destination: fragmentizedDestination }));
}
- // Make the link open in the immediate parent of the frame.
- $target.attr('target', '_parent');
+ // Make the link open in the immediate parent of the frame, unless the
+ // link already has a different target.
+ if (!$target.attr('target')) {
+ $target.attr('target', '_parent');
+ }
}
}
}
diff --git a/modules/overlay/overlay.info b/modules/overlay/overlay.info
index 5fb354d..f113064 100644
--- a/modules/overlay/overlay.info
+++ b/modules/overlay/overlay.info
@@ -3,3 +3,9 @@ description = Displays the Drupal administration interface in an overlay.
package = Core
version = VERSION
core = 7.x
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/overlay/overlay.module b/modules/overlay/overlay.module
index c07cc6c..7b2fc93 100644
--- a/modules/overlay/overlay.module
+++ b/modules/overlay/overlay.module
@@ -146,6 +146,10 @@ function overlay_init() {
// If this page shouldn't be rendered inside the overlay, redirect to the
// parent.
elseif (!path_is_admin($current_path)) {
+ // Prevent open redirects by ensuring the current path is not an absolute URL.
+ if (url_is_external($current_path)) {
+ $current_path = '';
+ }
overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render'))));
}
@@ -704,7 +708,7 @@ function overlay_overlay_child_initialize() {
}
/**
- * Requests that the overlay overlay closes when the page is displayed.
+ * Requests that the overlay closes when the page is displayed.
*
* @param $redirect
* (optional) The path that should open in the parent window after the
diff --git a/modules/path/path.info b/modules/path/path.info
index 8b6b67c..8e2b63b 100644
--- a/modules/path/path.info
+++ b/modules/path/path.info
@@ -5,3 +5,9 @@ version = VERSION
core = 7.x
files[] = path.test
configure = admin/config/search/path
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/path/path.test b/modules/path/path.test
index f5c303c..edecff5 100644
--- a/modules/path/path.test
+++ b/modules/path/path.test
@@ -42,12 +42,12 @@ class PathTestCase extends DrupalWebTestCase {
// created.
cache_clear_all('*', 'cache_path', TRUE);
$this->drupalGet($edit['source']);
- $this->assertTrue(cache_get($edit['source'], 'cache_path'), t('Cache entry was created.'));
+ $this->assertTrue(cache_get($edit['source'], 'cache_path'), 'Cache entry was created.');
// Visit the alias for the node and confirm a cache entry is created.
cache_clear_all('*', 'cache_path', TRUE);
$this->drupalGet($edit['alias']);
- $this->assertTrue(cache_get($edit['source'], 'cache_path'), t('Cache entry was created.'));
+ $this->assertTrue(cache_get($edit['source'], 'cache_path'), 'Cache entry was created.');
}
/**
@@ -334,7 +334,7 @@ class PathLanguageTestCase extends DrupalWebTestCase {
drupal_static_reset('locale_url_outbound_alter');
$languages = language_list();
$url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language]));
- $this->assertTrue(strpos($url, $edit['path[alias]']), t('URL contains the path alias.'));
+ $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.');
// Confirm that the alias works even when changing language negotiation
// options. Enable User language detection and selection over URL one.
@@ -378,23 +378,23 @@ class PathLanguageTestCase extends DrupalWebTestCase {
// situation only aliases in the default language and language neutral ones
// should keep working.
$this->drupalGet($french_alias);
- $this->assertResponse(404, t('Alias for French translation is unavailable when URL language negotiation is disabled.'));
+ $this->assertResponse(404, 'Alias for French translation is unavailable when URL language negotiation is disabled.');
// drupal_lookup_path() has an internal static cache. Check to see that
// it has the appropriate contents at this point.
drupal_lookup_path('wipe');
$french_node_path = drupal_lookup_path('source', $french_alias, $french_node->language);
- $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path works.'));
+ $this->assertEqual($french_node_path, 'node/' . $french_node->nid, 'Normal path works.');
// Second call should return the same path.
$french_node_path = drupal_lookup_path('source', $french_alias, $french_node->language);
- $this->assertEqual($french_node_path, 'node/' . $french_node->nid, t('Normal path is the same.'));
+ $this->assertEqual($french_node_path, 'node/' . $french_node->nid, 'Normal path is the same.');
// Confirm that the alias works.
$french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language);
- $this->assertEqual($french_node_alias, $french_alias, t('Alias works.'));
+ $this->assertEqual($french_node_alias, $french_alias, 'Alias works.');
// Second call should return the same alias.
$french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language);
- $this->assertEqual($french_node_alias, $french_alias, t('Alias is the same.'));
+ $this->assertEqual($french_node_alias, $french_alias, 'Alias is the same.');
}
}
@@ -508,8 +508,8 @@ class PathMonolingualTestCase extends DrupalWebTestCase {
$this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
// Verify that French is the only language.
- $this->assertFalse(drupal_multilingual(), t('Site is mono-lingual'));
- $this->assertEqual(language_default('language'), 'fr', t('French is the default language'));
+ $this->assertFalse(drupal_multilingual(), 'Site is mono-lingual');
+ $this->assertEqual(language_default('language'), 'fr', 'French is the default language');
// Set language detection to URL.
$edit = array('language[enabled][locale-url]' => TRUE);
diff --git a/modules/php/php.info b/modules/php/php.info
index cf86362..aea13e3 100644
--- a/modules/php/php.info
+++ b/modules/php/php.info
@@ -4,3 +4,9 @@ package = Core
version = VERSION
core = 7.x
files[] = php.test
+
+; Information added by Drupal.org packaging script on 2015-04-02
+version = "7.36"
+project = "drupal"
+datestamp = "1427943826"
+
diff --git a/modules/php/php.module b/modules/php/php.module
index 996f746..67616f6 100644
--- a/modules/php/php.module
+++ b/modules/php/php.module
@@ -17,7 +17,7 @@ function php_help($path, $arg) {
$output .= '' . t('Uses') . '
';
$output .= '';
$output .= '- ' . t('Enabling execution of PHP in text fields') . '
';
- $output .= '- ' . t('The PHP filter module allows users with the proper permissions to include custom PHP code that will get executed when pages of your site are processed. While this is a powerful and flexible feature if used by a trusted user with PHP experience, it is a significant and dangerous security risk in the hands of a malicious or inexperienced user. Even a trusted user may accidentally compromise the site by entering malformed or incorrect PHP code. Only the most trusted users should be granted permission to use the PHP filter, and all PHP code added through the PHP filter should be carefully examined before use. Example PHP snippets can be found on Drupal.org.', array('@php-snippets' => url('http://http://drupal.org/documentation/customization/php-snippets'))) . '
';
+ $output .= '- ' . t('The PHP filter module allows users with the proper permissions to include custom PHP code that will get executed when pages of your site are processed. While this is a powerful and flexible feature if used by a trusted user with PHP experience, it is a significant and dangerous security risk in the hands of a malicious or inexperienced user. Even a trusted user may accidentally compromise the site by entering malformed or incorrect PHP code. Only the most trusted users should be granted permission to use the PHP filter, and all PHP code added through the PHP filter should be carefully examined before use. Example PHP snippets can be found on Drupal.org.', array('@php-snippets' => url('http://drupal.org/documentation/customization/php-snippets'))) . '
';
$output .= '
';
return $output;
}
@@ -47,7 +47,7 @@ function php_permission() {
* overwrite any variables in the calling code, unlike a regular eval() call.
*
* This function is also used as an implementation of
- * hook_filter_FILTER_process().
+ * callback_filter_process().
*
* @param $code
* The code to evaluate.
@@ -88,7 +88,7 @@ function php_eval($code) {
}
/**
- * Implements hook_filter_FILTER_tips().
+ * Implements callback_filter_tips().
*
* @see php_filter_info()
*/
@@ -122,7 +122,7 @@ else {
print t(\'Welcome visitor! Thank you for visiting.\');
}
') . '';
- $output .= '' . t('Drupal.org offers some example PHP snippets, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://http://drupal.org/documentation/customization/php-snippets'))) . '
';
+ $output .= '' . t('Drupal.org offers some example PHP snippets, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://drupal.org/documentation/customization/php-snippets'))) . '
';
return $output;
}
else {
diff --git a/modules/php/php.test b/modules/php/php.test
index b68bd50..3b11b48 100644
--- a/modules/php/php.test
+++ b/modules/php/php.test
@@ -21,20 +21,20 @@ class PHPTestCase extends DrupalWebTestCase {
// Verify that the PHP code text format was inserted.
$php_format_id = 'php_code';
$this->php_code_format = filter_format_load($php_format_id);
- $this->assertEqual($this->php_code_format->name, 'PHP code', t('PHP code text format was created.'));
+ $this->assertEqual($this->php_code_format->name, 'PHP code', 'PHP code text format was created.');
// Verify that the format has the PHP code filter enabled.
$filters = filter_list_format($php_format_id);
- $this->assertTrue($filters['php_code']->status, t('PHP code filter is enabled.'));
+ $this->assertTrue($filters['php_code']->status, 'PHP code filter is enabled.');
// Verify that the format exists on the administration page.
$this->drupalGet('admin/config/content/formats');
- $this->assertText('PHP code', t('PHP code text format was created.'));
+ $this->assertText('PHP code', 'PHP code text format was created.');
// Verify that anonymous and authenticated user roles do not have access.
$this->drupalGet('admin/config/content/formats/' . $php_format_id);
- $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.'));
+ $this->assertFieldByName('roles[' . DRUPAL_ANONYMOUS_RID . ']', FALSE, 'Anonymous users do not have access to PHP code format.');
+ $this->assertFieldByName('roles[' . DRUPAL_AUTHENTICATED_RID . ']', FALSE, 'Authenticated users do not have access to PHP code format.');
}
/**
@@ -73,18 +73,18 @@ class PHPFilterTestCase extends PHPTestCase {
// Make sure that the PHP code shows up as text.
$this->drupalGet('node/' . $node->nid);
- $this->assertText('print "SimpleTest PHP was executed!"', t('PHP code is displayed.'));
+ $this->assertText('print "SimpleTest PHP was executed!"', 'PHP code is displayed.');
// Change filter to PHP filter and see that PHP code is evaluated.
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["body[$langcode][0][format]"] = $this->php_code_format->format;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
- $this->assertRaw(t('Basic page %title has been updated.', array('%title' => $node->title)), t('PHP code filter turned on.'));
+ $this->assertRaw(t('Basic page %title has been updated.', array('%title' => $node->title)), 'PHP code filter turned on.');
// Make sure that the PHP code shows up as text.
- $this->assertNoText('print "SimpleTest PHP was executed!"', t("PHP code isn't displayed."));
- $this->assertText('SimpleTest PHP was executed!', t('PHP code has been evaluated.'));
+ $this->assertNoText('print "SimpleTest PHP was executed!"', "PHP code isn't displayed.");
+ $this->assertText('SimpleTest PHP was executed!', 'PHP code has been evaluated.');
}
}
@@ -111,10 +111,10 @@ class PHPAccessTestCase extends PHPTestCase {
// Make sure that the PHP code shows up as text.
$this->drupalGet('node/' . $node->nid);
- $this->assertText('print', t('PHP code was not evaluated.'));
+ $this->assertText('print', 'PHP code was not evaluated.');
// Make sure that user doesn't have access to filter.
$this->drupalGet('node/' . $node->nid . '/edit');
- $this->assertNoRaw('