Browse Source

updated core to 7.53

Bachir Soussi Chiadmi 7 years ago
parent
commit
400dd6e9f4
100 changed files with 2296 additions and 527 deletions
  1. 323 29
      CHANGELOG.txt
  2. 1 1
      INSTALL.txt
  3. 106 113
      MAINTAINERS.txt
  4. 10 0
      UPGRADE.txt
  5. 38 3
      includes/ajax.inc
  6. 2 2
      includes/batch.inc
  7. 411 80
      includes/bootstrap.inc
  8. 1 0
      includes/cache.inc
  9. 200 44
      includes/common.inc
  10. 64 8
      includes/database/database.inc
  11. 58 5
      includes/database/mysql/database.inc
  12. 11 3
      includes/database/mysql/schema.inc
  13. 8 0
      includes/database/pgsql/database.inc
  14. 1 1
      includes/database/query.inc
  15. 4 1
      includes/database/schema.inc
  16. 15 0
      includes/database/select.inc
  17. 8 0
      includes/database/sqlite/database.inc
  18. 0 2
      includes/database/sqlite/install.inc
  19. 74 12
      includes/entity.inc
  20. 10 1
      includes/errors.inc
  21. 6 4
      includes/file.inc
  22. 146 26
      includes/form.inc
  23. 19 3
      includes/install.core.inc
  24. 9 1
      includes/install.inc
  25. 1 1
      includes/language.inc
  26. 57 7
      includes/locale.inc
  27. 1 1
      includes/mail.inc
  28. 67 8
      includes/menu.inc
  29. 76 14
      includes/module.inc
  30. 2 1
      includes/path.inc
  31. 1 1
      includes/registry.inc
  32. 4 2
      includes/session.inc
  33. 32 8
      includes/theme.inc
  34. 12 0
      includes/update.inc
  35. 8 0
      includes/xmlrpcs.inc
  36. 34 6
      misc/ajax.js
  37. 9 5
      misc/autocomplete.js
  38. 94 2
      misc/drupal.js
  39. BIN
      misc/favicon.ico
  40. 5 1
      misc/states.js
  41. 19 6
      misc/tabledrag.js
  42. 5 1
      misc/tableselect.js
  43. 6 0
      misc/vertical-tabs.js
  44. 3 3
      modules/aggregator/aggregator.info
  45. 1 1
      modules/aggregator/aggregator.processor.inc
  46. 3 3
      modules/aggregator/tests/aggregator_test.info
  47. 25 0
      modules/block/block.api.php
  48. 3 3
      modules/block/block.info
  49. 3 3
      modules/block/block.js
  50. 2 1
      modules/block/block.module
  51. 2 2
      modules/block/block.test
  52. 3 3
      modules/block/tests/block_test.info
  53. 3 3
      modules/block/tests/themes/block_test_theme/block_test_theme.info
  54. 3 3
      modules/blog/blog.info
  55. 1 1
      modules/blog/blog.module
  56. 3 3
      modules/book/book.info
  57. 3 3
      modules/color/color.info
  58. 3 3
      modules/comment/comment.info
  59. 5 2
      modules/comment/comment.module
  60. 36 1
      modules/comment/comment.test
  61. 3 3
      modules/contact/contact.info
  62. 2 2
      modules/contact/contact.pages.inc
  63. 3 3
      modules/contextual/contextual.info
  64. 3 3
      modules/dashboard/dashboard.info
  65. 8 1
      modules/dblog/dblog.admin.inc
  66. 3 3
      modules/dblog/dblog.info
  67. 9 0
      modules/dblog/dblog.install
  68. 24 14
      modules/dblog/dblog.module
  69. 3 0
      modules/dblog/dblog.test
  70. 1 1
      modules/field/field.api.php
  71. 3 3
      modules/field/field.crud.inc
  72. 3 3
      modules/field/field.info
  73. 3 1
      modules/field/field.info.class.inc
  74. 21 0
      modules/field/field.install
  75. 16 0
      modules/field/field.module
  76. 3 3
      modules/field/modules/field_sql_storage/field_sql_storage.info
  77. 23 3
      modules/field/modules/field_sql_storage/field_sql_storage.module
  78. 2 2
      modules/field/modules/field_sql_storage/field_sql_storage.test
  79. 3 3
      modules/field/modules/list/list.info
  80. 1 1
      modules/field/modules/list/tests/list.test
  81. 3 3
      modules/field/modules/list/tests/list_test.info
  82. 3 3
      modules/field/modules/number/number.info
  83. 4 2
      modules/field/modules/number/number.module
  84. 1 1
      modules/field/modules/number/number.test
  85. 3 3
      modules/field/modules/options/options.info
  86. 9 1
      modules/field/modules/options/options.module
  87. 4 3
      modules/field/modules/options/options.test
  88. 3 3
      modules/field/modules/text/text.info
  89. 4 2
      modules/field/modules/text/text.module
  90. 1 0
      modules/field/modules/text/text.test
  91. 2 1
      modules/field/tests/field.test
  92. 3 3
      modules/field/tests/field_test.info
  93. 4 0
      modules/field/tests/field_test.module
  94. 4 0
      modules/field_ui/field_ui.admin.inc
  95. 3 3
      modules/field_ui/field_ui.info
  96. 2 2
      modules/field_ui/field_ui.js
  97. 21 1
      modules/field_ui/field_ui.module
  98. 15 2
      modules/field_ui/field_ui.test
  99. 7 1
      modules/file/file.field.inc
  100. 3 3
      modules/file/file.info

+ 323 - 29
CHANGELOG.txt

@@ -1,6 +1,300 @@
 
 
+Drupal 7.53, 2016-12-07
+-----------------------
+- Fixed drag and drop support on newer Chrome/IE 11+ versions after 7.51 update
+  when jQuery is updated to 1.7-1.11.0.
+
+Drupal 7.52, 2016-11-16
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-005.
+
+Drupal 7.51, 2016-10-05
+-----------------------
+- The Update module now also checks for updates to a disabled theme that is
+  used as an admin theme.
+- Exceptions thrown in dblog_watchdog() are now caught and ignored.
+- Clarified the warning that appears when modules are missing or have moved.
+- Log messages are now XSS filtered on display.
+- Draggable tables now work on touch screen devices.
+- Added a setting for allowing double underscores in CSS identifiers
+  (https://www.drupal.org/node/2810369).
+- If a user navigates away from a page while an Ajax request is running they
+  will no longer get an error message saying "An Ajax HTTP request terminated
+  abnormally".
+- The system_region_list() API function now takes an optional third parameter
+  which allows region name translations to be skipped when they are not needed
+  (API addition: https://www.drupal.org/node/2810365).
+- Numerous performance improvements.
+- Numerous bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.50, 2016-07-07 
+-----------------------
+- Added a new "administer fields" permission for trusted users, which is
+  required in addition to other permissions to use the field UI
+  (https://www.drupal.org/node/2483307).
+- Added clickjacking protection to Drupal core by setting the X-Frame-Options
+  header to SAMEORIGIN by default (https://www.drupal.org/node/2735873).
+- Added support for full UTF-8 (emojis, Asian symbols, mathematical symbols) on
+  MySQL and other database drivers when the site and database are configured to
+  allow it (https://www.drupal.org/node/2761183).
+- Improved performance by avoiding a re-scan of directories when a file is
+  missing; instead, trigger a PHP warning (minor API change:
+  https://www.drupal.org/node/2581445).
+- Made it possible to use any PHP callable in Ajax form callbacks, form API
+  form-building functions, and form API wrapper callbacks (API addition:
+  https://www.drupal.org/node/2761169).
+- Fixed that following a password reset link while logged in leaves users unable
+  to change their password (minor user interface change:
+  https://www.drupal.org/node/2759023).
+- Implemented various fixes for automated test failures on PHP 5.4+ and PHP 7.
+  Drupal core automated tests now pass in these environments.
+- Improved support for PHP 7 by fixing various problems.
+- Fixed various bugs with PHP 5.5+ imagerotate(), including when incorrect
+  color indices are passed in.
+- Fixed a regression introduced in Drupal 7.43 that allowed files uploaded by
+  anonymous users to be lost after form validation errors, and that also caused
+  regressions with certain contributed modules.
+- Fixed a regression introduced in Drupal 7.36 which caused the default value
+  of hidden textarea fields to be ignored.
+- Fixed robots.txt to allow search engines to access CSS, JavaScript and image
+  files.
+- Changed wording on the Update Manager settings page to clarify that the
+  option to check for disabled module updates also applies to uninstalled
+  modules (administrative-facing translatable string change).
+- Changed the help text when editing menu links and configuring URL redirect
+  actions so that it does not reference "Drupal" or the drupal.org website
+  (administrative-facing translatable string change).
+- Fixed the locale safety check that is used to ensure that translations are
+  safe to allow for tokens in the href/src attributes of translated strings.
+- Fixed that URL generation only works on port 80 when using domain based
+  language negotation.
+- Made method="get" forms work inside the administrative overlay. The fix adds
+  a new hidden field to these forms when they appear inside the overlay (minor
+  data structure change).
+- Increased maxlength of menu link title input fields in the node form and
+  menu link form from 128 to 255 characters.
+- Removed meaningless post-check=0 and pre-check=0 cache control headers from
+  Drupal HTTP responses.
+- Added a .editorconfig file to auto-configure editors that support it.
+- Added --directory option to run-tests.sh for easier test discovery of all
+  tests within a project.
+- Made run-tests.sh exit with a failure code when there are test fails or
+  problems running the script.
+- Fixed that cookies from previous tests are still present when a new test
+  starts in DrupalWebTestCase.
+- Improved performance of queries on the {authmap} database table.
+- Fixed handling of missing files and functions inside the registry.
+- Fixed Ajax handling for tableselect form elements that use checkboxes.
+- Fixed a bug which caused ip_address() to return nothing when the client IP
+  address and proxy IP address are the same.
+- Added a new option to format_xml_elements() to allow for already encoded
+  values.
+- Changed the {history} table's node ID field to be an unsigned integer, to
+  match the same field in the {node} table and to prevent errors with very
+  large node IDs.
+- Added an explicit page callback to the "admin/people/create" menu item in the
+  User module (minor data structure change). Previously this automatically
+  inherited the page callback from the parent "admin/people" menu item, which
+  broke contributed modules that override the "admin/people" page.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.44, 2016-06-15
+-----------------------
+- Fixed security issues (privilege escalation). See SA-CORE-2016-002.
+
+Drupal 7.43, 2016-02-24
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-001.
+
+Drupal 7.42, 2016-02-03
+-----------------------
+- Stopped invoking hook_flush_caches() on every cron run, since some modules
+  use that hook for expensive operations that are only needed on cache clears.
+- Changed the default .htaccess and web.config to block Composer-related files.
+- Added static caching to module_load_include() to improve performance.
+- Fixed double-encoding bugs in select field widgets provided by the Options
+  module. The fix deprecates the 'strip_tags' property on option widgets and
+  replaces it with a new 'strip_tags_and_unescape' property (minor data
+  structure change).
+- Improved MySQL 5.7 support by changing the MySQL database driver to stop
+  using the ANSI SQL mode alias, which has different meanings for different
+  MySQL versions.
+- Fixed a regression introduced in Drupal 7.39 which prevented autocomplete
+  functionality from working on servers that are not configured to
+  automatically recognize index.php.
+- Updated the Archive_Tar PEAR package to the latest 1.4.0 release, to fix bugs
+  with tar file handling on various operating systems.
+- Fixed fatal errors on node preview when a field is displayed in the node
+  teaser but hidden in the full node view. The fix removes a
+  field_attach_prepare_view() call from the node_preview() function since it is
+  redundant with one in the node preview theme layer.
+- Improved the description of the "Trimmed" format option on text fields
+  (translatable string change, and minor UI and data structure change).
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.41, 2015-10-21
+-----------------------
+- Fixed security issues (open redirect). See SA-CORE-2015-004.
+
+Drupal 7.40, 2015-10-14
+-----------------------
+- Made Drupal's code for parsing .info files run much faster and use much less
+  memory.
+- Prevented drupal_http_request() from returning an error when it receives a
+  201 through 206 HTTP status code.
+- Added support for autoloading traits via the registry on sites running PHP
+  5.4 or higher.
+- Allowed the user-picture.tpl.php theme template to have HTML classes besides
+  the default "user-picture" class printed in it (markup change).
+- Fixed the URL text filter to convert e-mail addresses with plus signs into
+  mailto: links.
+- Added alternate text to file icons displayed by the File module, to improve
+  accessibility (string change, and minor API addition to theme_file_icon()).
+- Changed one-time login link failure messages to be displayed as errors or
+  warnings as appropriate, rather than as regular status messages (minor UI
+  change and data structure change).
+- Changed the default settings.php configuration to exclude private files from
+  the "404_fast_paths" behavior.
+- Changed the page that displays filter tips for a particular text format, for
+  example filter/tips/full_html, to return "page not found" or "access denied"
+  if the format does not exist or the user does not have access to it. This
+  change adds a new menu item to the Filter module's hook_menu() entry (minor
+  data structure change).
+- Added a new hook, hook_block_cid_parts_alter(), to allow modules to alter the
+  cache keys used for caching a particular block.
+- Made drupal_set_message() display and return messages when "0" is passed in
+  as the message to set.
+- Fixed non-functional "Files displayed by default" setting on file fields.
+- The "worker callback" provided in hook_cron_queue_info() and the "finished"
+  callback specified during batch processing can now be any PHP callable
+  instead of just functions.
+- Prevented drupal_set_time_limit() from decreasing the time limit in the case
+  where the PHP maximum execution time is already unlimited.
+- Changed the default thousand marker for numeric fields from a space ("1 000")
+  to nothing ("1000") (minor UI change: https://www.drupal.org/node/1388376).
+- Prevented malformed theme .info files (without a "name" key) from causing
+  exceptions during menu rebuilds. If an .info file without a "name" key is
+  found in a module or theme directory, Drupal will now use the module or
+  theme's machine name as the display name instead.
+- Made the format column in the {date_format_locale} database table
+  case-sensitive, to match the equivalent column in the {date_formats} table.
+- Fixed a bug in the Statistics module that caused JavaScript files attached to
+  a node while it is being viewed to be omitted from the page.
+- Added an optional 'project:' prefix that can be added to dependencies in a
+  module's .info file to indicate which project the dependency resides in (API
+  addition: https://www.drupal.org/node/2299747).
+- Fixed various bugs that occurred after hooks were invoked early in the Drupal
+  bootstrap and that caused module_implements() and drupal_alter() to cache an
+  incomplete set of hook implementations for later use.
+- Set the X-Content-Type-Options header to "nosniff" when possible, to prevent
+  certain web browsers from picking an unsafe MIME type.
+- Prevented the database API from executing multiple queries at once on MySQL,
+  if the site's PHP version is new enough to do so. This is a secondary defense
+  against SQL injection (API change: https://www.drupal.org/node/2463973).
+- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused the upgrade
+  to fail when there were multiple file records pointing to the same file.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.39, 2015-08-19
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-003.
+
+Drupal 7.38, 2015-06-17
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-002.
+
+Drupal 7.37, 2015-05-07
+-----------------------
+- Fixed a regression in Drupal 7.36 which caused certain kinds of content types
+  to become disabled if they were defined by a no-longer-enabled module.
+- Removed a confusing description regarding automatic time zone detection from
+  the user account form (minor UI and data structure change).
+- Allowed custom HTML tags with a dash in the name to pass through filter_xss()
+  when specified in the list of allowed tags.
+- Allowed hook_field_schema() implementations to specify indexes for fields
+  based on a fixed-length column prefix (rather than the entire column), as was
+  already allowed in hook_schema() implementations.
+- Fixed PDO exceptions on PostgreSQL when accessing invalid entity URLs.
+- Added a sites/all/libraries folder to the codebase, with instructions for
+  using it.
+- Added a description to the "Administer text formats and filters" permission
+  on the Permissions page (string change).
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+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
 Drupal 7.34, 2014-11-19
-----------------------
+-----------------------
 - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-006.
 - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-006.
 
 
 Drupal 7.33, 2014-11-07
 Drupal 7.33, 2014-11-07
@@ -69,11 +363,11 @@ Drupal 7.33, 2014-11-07
 - Additional automated test coverage.
 - Additional automated test coverage.
 
 
 Drupal 7.32, 2014-10-15
 Drupal 7.32, 2014-10-15
-----------------------
+-----------------------
 - Fixed security issues (SQL injection). See SA-CORE-2014-005.
 - Fixed security issues (SQL injection). See SA-CORE-2014-005.
 
 
 Drupal 7.31, 2014-08-06
 Drupal 7.31, 2014-08-06
-----------------------
+-----------------------
 - Fixed security issues (denial of service). See SA-CORE-2014-004.
 - Fixed security issues (denial of service). See SA-CORE-2014-004.
 
 
 Drupal 7.30, 2014-07-24
 Drupal 7.30, 2014-07-24
@@ -88,7 +382,7 @@ Drupal 7.30, 2014-07-24
 - Additional automated test coverage.
 - Additional automated test coverage.
 
 
 Drupal 7.29, 2014-07-16
 Drupal 7.29, 2014-07-16
-----------------------
+-----------------------
 - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003.
 - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003.
 
 
 Drupal 7.28, 2014-05-08
 Drupal 7.28, 2014-05-08
@@ -134,11 +428,11 @@ Drupal 7.28, 2014-05-08
 - Additional automated test coverage.
 - Additional automated test coverage.
 
 
 Drupal 7.27, 2014-04-16
 Drupal 7.27, 2014-04-16
-----------------------
+-----------------------
 - Fixed security issues (information disclosure). See SA-CORE-2014-002.
 - Fixed security issues (information disclosure). See SA-CORE-2014-002.
 
 
 Drupal 7.26, 2014-01-15
 Drupal 7.26, 2014-01-15
-----------------------
+-----------------------
 - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001.
 - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001.
 
 
 Drupal 7.25, 2014-01-02
 Drupal 7.25, 2014-01-02
@@ -204,7 +498,7 @@ Drupal 7.25, 2014-01-02
 - Additional automated test coverage.
 - Additional automated test coverage.
 
 
 Drupal 7.24, 2013-11-20
 Drupal 7.24, 2013-11-20
-----------------------
+-----------------------
 - Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
 - Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
 
 
 Drupal 7.23, 2013-08-07
 Drupal 7.23, 2013-08-07
@@ -458,8 +752,8 @@ Drupal 7.15, 2012-08-01
 - Numerous API documentation improvements.
 - Numerous API documentation improvements.
 - Additional automated test coverage.
 - Additional automated test coverage.
 
 
-Drupal 7.14 2012-05-02
-----------------------
+Drupal 7.14, 2012-05-02
+-----------------------
 - Fixed "integrity constraint" fatal errors when rebuilding registry.
 - Fixed "integrity constraint" fatal errors when rebuilding registry.
 - Fixed custom logo and favicon functionality referencing incorrect paths.
 - Fixed custom logo and favicon functionality referencing incorrect paths.
 - Fixed DB Case Sensitivity: Allow BINARY attribute in MySQL.
 - Fixed DB Case Sensitivity: Allow BINARY attribute in MySQL.
@@ -507,12 +801,12 @@ Drupal 7.14 2012-05-02
   - system_update_7061() converts filepaths too aggressively.
   - system_update_7061() converts filepaths too aggressively.
   - Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25.
   - Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25.
 
 
-Drupal 7.13 2012-05-02
-----------------------
+Drupal 7.13, 2012-05-02
+-----------------------
 - Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002.
 - Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002.
 
 
 Drupal 7.12, 2012-02-01
 Drupal 7.12, 2012-02-01
-----------------------
+-----------------------
 - Fixed bug preventing custom menus from receiving an active trail.
 - Fixed bug preventing custom menus from receiving an active trail.
 - Fixed hook_field_delete() no longer invoked during field_purge_data().
 - Fixed hook_field_delete() no longer invoked during field_purge_data().
 - Fixed bug causing entity info cache to not be cleared with the rest of caches.
 - Fixed bug causing entity info cache to not be cleared with the rest of caches.
@@ -546,11 +840,11 @@ Drupal 7.12, 2012-02-01
   cache.
   cache.
 
 
 Drupal 7.11, 2012-02-01
 Drupal 7.11, 2012-02-01
-----------------------
+-----------------------
 - Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
 - Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
 
 
 Drupal 7.10, 2011-12-05
 Drupal 7.10, 2011-12-05
-----------------------
+-----------------------
 - Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
 - Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
 - Reduce memory usage of theme registry (performance).
 - Reduce memory usage of theme registry (performance).
 - Fixed PECL upload progress bar for FileField
 - Fixed PECL upload progress bar for FileField
@@ -903,7 +1197,7 @@ Drupal 7.0, 2011-01-05
       requests.
       requests.
 
 
 Drupal 6.23-dev, xxxx-xx-xx (development release)
 Drupal 6.23-dev, xxxx-xx-xx (development release)
------------------------
+---------------------------
 
 
 Drupal 6.22, 2011-05-25
 Drupal 6.22, 2011-05-25
 -----------------------
 -----------------------
@@ -913,25 +1207,25 @@ Drupal 6.22, 2011-05-25
 - Fixed a variety of other bugs.
 - Fixed a variety of other bugs.
 
 
 Drupal 6.21, 2011-05-25
 Drupal 6.21, 2011-05-25
-----------------------
+-----------------------
 - Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
 - Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
 
 
 Drupal 6.20, 2010-12-15
 Drupal 6.20, 2010-12-15
-----------------------
+-----------------------
 - Fixed a variety of small bugs, improved code documentation.
 - Fixed a variety of small bugs, improved code documentation.
 
 
 Drupal 6.19, 2010-08-11
 Drupal 6.19, 2010-08-11
-----------------------
+-----------------------
 - Fixed a variety of small bugs, improved code documentation.
 - Fixed a variety of small bugs, improved code documentation.
 
 
 Drupal 6.18, 2010-08-11
 Drupal 6.18, 2010-08-11
-----------------------
+-----------------------
 - Fixed security issues (OpenID authentication bypass, File download access
 - Fixed security issues (OpenID authentication bypass, File download access
   bypass, Comment unpublishing bypass, Actions cross site scripting),
   bypass, Comment unpublishing bypass, Actions cross site scripting),
   see SA-CORE-2010-002.
   see SA-CORE-2010-002.
 
 
 Drupal 6.17, 2010-06-02
 Drupal 6.17, 2010-06-02
-----------------------
+-----------------------
 - Improved PostgreSQL compatibility
 - Improved PostgreSQL compatibility
 - Better PHP 5.3 and PHP 4 compatibility
 - Better PHP 5.3 and PHP 4 compatibility
 - Better browser compatibility of CSS and JS aggregation
 - Better browser compatibility of CSS and JS aggregation
@@ -940,7 +1234,7 @@ Drupal 6.17, 2010-06-02
 - Fixed a variety of other bugs.
 - Fixed a variety of other bugs.
 
 
 Drupal 6.16, 2010-03-03
 Drupal 6.16, 2010-03-03
-----------------------
+-----------------------
 - Fixed security issues (Installation cross site scripting, Open redirection,
 - Fixed security issues (Installation cross site scripting, Open redirection,
   Locale module cross site scripting, Blocked user session regeneration),
   Locale module cross site scripting, Blocked user session regeneration),
   see SA-CORE-2010-001.
   see SA-CORE-2010-001.
@@ -952,12 +1246,12 @@ Drupal 6.16, 2010-03-03
 - Fixed a variety of other bugs.
 - Fixed a variety of other bugs.
 
 
 Drupal 6.15, 2009-12-16
 Drupal 6.15, 2009-12-16
-----------------------
+-----------------------
 - Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
 - Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
 - Fixed a variety of other bugs.
 - Fixed a variety of other bugs.
 
 
 Drupal 6.14, 2009-09-16
 Drupal 6.14, 2009-09-16
-----------------------
+-----------------------
 - Fixed security issues (OpenID association cross site request forgeries,
 - Fixed security issues (OpenID association cross site request forgeries,
   OpenID impersonation and File upload), see SA-CORE-2009-008.
   OpenID impersonation and File upload), see SA-CORE-2009-008.
 - Changed the system modules page to not run all cache rebuilds; use the
 - Changed the system modules page to not run all cache rebuilds; use the
@@ -966,18 +1260,18 @@ Drupal 6.14, 2009-09-16
 - Fixed a variety of small bugs.
 - Fixed a variety of small bugs.
 
 
 Drupal 6.13, 2009-07-01
 Drupal 6.13, 2009-07-01
-----------------------
+-----------------------
 - Fixed security issues (Cross site scripting, Input format access bypass and
 - Fixed security issues (Cross site scripting, Input format access bypass and
   Password leakage in URL), see SA-CORE-2009-007.
   Password leakage in URL), see SA-CORE-2009-007.
 - Fixed a variety of small bugs.
 - Fixed a variety of small bugs.
 
 
 Drupal 6.12, 2009-05-13
 Drupal 6.12, 2009-05-13
-----------------------
+-----------------------
 - Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
 - Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
 - Fixed a variety of small bugs.
 - Fixed a variety of small bugs.
 
 
 Drupal 6.11, 2009-04-29
 Drupal 6.11, 2009-04-29
-----------------------
+-----------------------
 - Fixed security issues (Cross site scripting and limited information
 - Fixed security issues (Cross site scripting and limited information
   disclosure), see SA-CORE-2009-005
   disclosure), see SA-CORE-2009-005
 - Fixed performance issues with the menu router cache, the update
 - Fixed performance issues with the menu router cache, the update
@@ -985,7 +1279,7 @@ Drupal 6.11, 2009-04-29
 - Fixed a variety of small bugs.
 - Fixed a variety of small bugs.
 
 
 Drupal 6.10, 2009-02-25
 Drupal 6.10, 2009-02-25
-----------------------
+-----------------------
 - Fixed a security issue, (Local file inclusion on Windows),
 - Fixed a security issue, (Local file inclusion on Windows),
   see SA-CORE-2009-003
   see SA-CORE-2009-003
 - Fixed node_feed() so custom fields can show up in RSS feeds.
 - Fixed node_feed() so custom fields can show up in RSS feeds.
@@ -1381,7 +1675,7 @@ Drupal 4.7.9, 2007-12-05
 - fixed a security issue (SQL injection), see SA-2007-031
 - fixed a security issue (SQL injection), see SA-2007-031
 
 
 Drupal 4.7.8, 2007-10-17
 Drupal 4.7.8, 2007-10-17
-----------------------
+------------------------
 - fixed a security issue (HTTP response splitting), see SA-2007-024
 - fixed a security issue (HTTP response splitting), see SA-2007-024
 - fixed a security issue (Cross site scripting via uploads), see SA-2007-026
 - fixed a security issue (Cross site scripting via uploads), see SA-2007-026
 - fixed a security issue (API handling of unpublished comment), see SA-2007-030
 - fixed a security issue (API handling of unpublished comment), see SA-2007-030
@@ -1494,7 +1788,7 @@ Drupal 4.6.11, 2007-01-05
 - Fixed security issue (DoS), see SA-2007-002
 - Fixed security issue (DoS), see SA-2007-002
 
 
 Drupal 4.6.10, 2006-10-18
 Drupal 4.6.10, 2006-10-18
-------------------------
+-------------------------
 - Fixed security issue (XSS), see SA-2006-024
 - Fixed security issue (XSS), see SA-2006-024
 - Fixed security issue (CSRF), see SA-2006-025
 - Fixed security issue (CSRF), see SA-2006-025
 - Fixed security issue (Form action attribute injection), see SA-2006-026
 - Fixed security issue (Form action attribute injection), see SA-2006-026

+ 1 - 1
INSTALL.txt

@@ -23,7 +23,7 @@ Drupal requires:
   - Percona Server 5.1.70 (or greater) (http://www.percona.com/). Percona
   - Percona Server 5.1.70 (or greater) (http://www.percona.com/). Percona
     Server is a backwards-compatible replacement for MySQL.
     Server is a backwards-compatible replacement for MySQL.
   - PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
   - PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
-  - SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
+  - SQLite 3.3.7 (or greater) (http://www.sqlite.org/).
 
 
 For more detailed information about Drupal requirements, including a list of
 For more detailed information about Drupal requirements, including a list of
 PHP extensions and configurations that are required, see "System requirements"
 PHP extensions and configurations that are required, see "System requirements"

+ 106 - 113
MAINTAINERS.txt

@@ -1,7 +1,8 @@
 
 
 Drupal core is built and maintained by the Drupal project community. Everyone is
 Drupal core is built and maintained by the Drupal project community. Everyone is
 encouraged to submit issues and changes (patches) to improve Drupal, and to
 encouraged to submit issues and changes (patches) to improve Drupal, and to
-contribute in other ways -- see http://drupal.org/contribute to find out how.
+contribute in other ways -- see https://www.drupal.org/contribute to find out
+how.
 
 
 Branch maintainers
 Branch maintainers
 ------------------
 ------------------
@@ -9,160 +10,154 @@ Branch maintainers
 The Drupal Core branch maintainers oversee the development of Drupal as a whole.
 The Drupal Core branch maintainers oversee the development of Drupal as a whole.
 The branch maintainers for Drupal 7 are:
 The branch maintainers for Drupal 7 are:
 
 
-- Dries Buytaert 'dries' http://drupal.org/user/1
-- Angela Byron 'webchick' http://drupal.org/user/24967
-- David Rothstein 'David_Rothstein' http://drupal.org/user/124982
+- Dries Buytaert 'dries' https://www.drupal.org/u/dries
+- Angela Byron 'webchick' https://www.drupal.org/u/webchick
+- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
+- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
+- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0
 
 
 
 
 Component maintainers
 Component maintainers
 ---------------------
 ---------------------
 
 
 The Drupal Core component maintainers oversee the development of Drupal
 The Drupal Core component maintainers oversee the development of Drupal
-subsystems. See http://drupal.org/contribute/core-maintainers for more
+subsystems. See https://www.drupal.org/contribute/core-maintainers for more
 information on their responsibilities, and to find out how to become a component
 information on their responsibilities, and to find out how to become a component
 maintainer. Current component maintainers for Drupal 7:
 maintainer. Current component maintainers for Drupal 7:
 
 
 Ajax system
 Ajax system
-- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
-- Earl Miles 'merlinofchaos' http://drupal.org/user/26979
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
 
 
 Base system
 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
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
 
 
 Batch system
 Batch system
-- Yves Chedemois 'yched' http://drupal.org/user/39567
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
 
 
 Cache system
 Cache system
-- Damien Tournoud 'DamZ' http://drupal.org/user/22211
-- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
 
 
 Cron system
 Cron system
-- Károly Négyesi 'chx' http://drupal.org/user/9446
-- Derek Wright 'dww' http://drupal.org/user/46549
+- Derek Wright 'dww' https://www.drupal.org/u/dww
 
 
 Database system
 Database system
-- Larry Garfield 'Crell' http://drupal.org/user/26398
+- Larry Garfield 'Crell' https://www.drupal.org/u/crell
 
 
   - MySQL driver
   - MySQL driver
-    - Larry Garfield 'Crell' http://drupal.org/user/26398
-    - David Strauss 'David Strauss' http://drupal.org/user/93254
+    - Larry Garfield 'Crell' https://www.drupal.org/u/crell
+    - David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
 
 
   - PostgreSQL driver
   - PostgreSQL driver
-    - Damien Tournoud 'DamZ' http://drupal.org/user/22211
-    - Josh Waihi 'fiasco' http://drupal.org/user/188162
+    - Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
+    - Josh Waihi 'fiasco' https://www.drupal.org/u/josh-waihi
 
 
   - Sqlite driver
   - Sqlite driver
-    - Damien Tournoud 'DamZ' http://drupal.org/user/22211
-    - Károly Négyesi 'chx' http://drupal.org/user/9446
+    - Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
 
 
 Database update system
 Database update system
-- Károly Négyesi 'chx' http://drupal.org/user/9446
-- Ashok Modi 'BTMash' http://drupal.org/user/60422
+- Ashok Modi 'BTMash' https://www.drupal.org/u/btmash
 
 
 Entity system
 Entity system
-- Wolfgang Ziegler 'fago' http://drupal.org/user/16747
-- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
-- Franz Heinzmann 'Frando' http://drupal.org/user/21850
+- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
 
 
 File system
 File system
-- Andrew Morton 'drewish' http://drupal.org/user/34869
-- Aaron Winborn 'aaron' http://drupal.org/user/33420
+- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
+- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
 
 
 Form system
 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
-- Franz Heinzmann 'Frando' http://drupal.org/user/21850
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
+- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
 
 
 Image system
 Image system
-- Andrew Morton 'drewish' http://drupal.org/user/34869
-- Nathan Haug 'quicksketch' http://drupal.org/user/35821
+- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
+- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
 
 
 Install system
 Install system
-- David Rothstein 'David_Rothstein' http://drupal.org/user/124982
+- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
 
 
 JavaScript
 JavaScript
-- Théodore Biadala 'nod_' http://drupal.org/user/598310
-- Steve De Jonghe 'seutje' http://drupal.org/user/264148
-- Jesse Renée Beach 'jessebeach' http://drupal.org/user/748566
+- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
+- Steve De Jonghe 'seutje' https://www.drupal.org/u/seutje
 
 
 Language system
 Language system
-- Francesco Placella 'plach' http://drupal.org/user/183211
-- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
 
 
 Lock system
 Lock system
-- Damien Tournoud 'DamZ' http://drupal.org/user/22211
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
 
 
 Mail system
 Mail system
 - ?
 - ?
 
 
 Markup
 Markup
-- Jacine Luisi 'Jacine' http://drupal.org/user/88931
-- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
+- Jacine Luisi 'Jacine' https://www.drupal.org/u/jacine
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
 
 
 Menu system
 Menu system
-- Peter Wolanin 'pwolanin' http://drupal.org/user/49851
-- Károly Négyesi 'chx' http://drupal.org/user/9446
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
 
 
 Path system
 Path system
-- Dave Reid 'davereid' http://drupal.org/user/53892
-- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
 
 
 Render system
 Render system
-- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
-- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
-- Franz Heinzmann 'Frando' http://drupal.org/user/21850
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
 
 
 Theme system
 Theme system
-- Earl Miles 'merlinofchaos' http://drupal.org/user/26979
-- Alex Bronstein 'effulgentsia' http://drupal.org/user/78040
-- Joon Park 'dvessel' http://drupal.org/user/56782
-- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
+- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Joon Park 'dvessel' https://www.drupal.org/u/dvessel
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
 
 
 Token system
 Token system
-- Dave Reid 'davereid' http://drupal.org/user/53892
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
 
 
 XML-RPC system
 XML-RPC system
-- Frederic G. Marand 'fgm' http://drupal.org/user/27985
+- Frederic G. Marand 'fgm' https://www.drupal.org/u/fgm
 
 
 
 
 Topic coordinators
 Topic coordinators
 ------------------
 ------------------
 
 
 Accessibility
 Accessibility
-- Everett Zufelt 'Everett Zufelt' http://drupal.org/user/406552
-- Brandon Bowersox-Johnson 'bowersox' http://drupal.org/user/186415
+- Everett Zufelt 'Everett Zufelt' https://www.drupal.org/u/everett-zufelt
+- Brandon Bowersox-Johnson 'bowersox' https://www.drupal.org/u/bowersox
 
 
 Documentation
 Documentation
-- Jennifer Hodgdon 'jhodgdon' http://drupal.org/user/155601
+- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon
 
 
 Translations
 Translations
-- Gerhard Killesreiter 'killes' http://drupal.org/user/83
+- Gerhard Killesreiter 'killes' https://www.drupal.org/u/gerhard-killesreiter
 
 
 User experience and usability
 User experience and usability
-- Roy Scholten 'yoroy' http://drupal.org/user/41502
-- Bojhan Somers 'Bojhan' http://drupal.org/user/87969
+- Roy Scholten 'yoroy' https://www.drupal.org/u/yoroy
+- Bojhan Somers 'Bojhan' https://www.drupal.org/u/bojhan
 
 
 Node Access
 Node Access
-- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
-- Ken Rickard 'agentrickard' http://drupal.org/user/20975
-- Jess Myrbo 'xjm' http://drupal.org/user/65776
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
 
 
 
 
 Security team
 Security team
 -----------------
 -----------------
 
 
-To report a security issue, see: https://drupal.org/security-team/report-issue
+To report a security issue, see: https://www.drupal.org/security-team/report-issue
 
 
 The Drupal security team provides Security Advisories for vulnerabilities,
 The Drupal security team provides Security Advisories for vulnerabilities,
 assists developers in resolving security issues, and provides security
 assists developers in resolving security issues, and provides security
-documentation. See http://drupal.org/security-team for more information. The
-security team lead is:
+documentation. See https://www.drupal.org/security-team for more information.
+The security team lead is:
 
 
-- Michael Hess 'mlhess' https://drupal.org/user/102818
+- Michael Hess 'mlhess' https://www.drupal.org/u/mlhess
 
 
 
 
 Module maintainers
 Module maintainers
@@ -172,143 +167,141 @@ Aggregator module
 - ?
 - ?
 
 
 Block module
 Block module
-- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
 
 
 Blog module
 Blog module
 - ?
 - ?
 
 
 Book module
 Book module
-- Peter Wolanin 'pwolanin' http://drupal.org/user/49851
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
 
 
 Color module
 Color module
 - ?
 - ?
 
 
 Comment module
 Comment module
-- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
 
 
 Contact module
 Contact module
-- Dave Reid 'davereid' http://drupal.org/user/53892
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
 
 
 Contextual module
 Contextual module
-- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
 
 
 Dashboard module
 Dashboard module
 - ?
 - ?
 
 
 Database logging module
 Database logging module
-- Khalid Baheyeldin 'kbahey' http://drupal.org/user/4063
+- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
 
 
 Field module
 Field module
-- Yves Chedemois 'yched' http://drupal.org/user/39567
-- Barry Jaspan 'bjaspan' http://drupal.org/user/46413
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
+- Barry Jaspan 'bjaspan' https://www.drupal.org/u/bjaspan
 
 
 Field UI module
 Field UI module
-- Yves Chedemois 'yched' http://drupal.org/user/39567
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
 
 
 File module
 File module
-- Aaron Winborn 'aaron' http://drupal.org/user/33420
+- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
 
 
 Filter module
 Filter module
-- Daniel F. Kudwien 'sun' http://drupal.org/user/54136
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
 
 
 Forum module
 Forum module
-- Lee Rowlands 'larowlan' http://drupal.org/user/395439
+- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
 
 
 Help module
 Help module
 - ?
 - ?
 
 
 Image module
 Image module
-- Nathan Haug 'quicksketch' http://drupal.org/user/35821
+- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
 
 
 Locale module
 Locale module
-- Gábor Hojtsy 'Gábor Hojtsy' http://drupal.org/user/4166
+- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
 
 
 Menu module
 Menu module
 - ?
 - ?
 
 
 Node module
 Node module
-- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
-- David Strauss 'David Strauss' http://drupal.org/user/93254
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
 
 
 OpenID module
 OpenID module
-- Vojtech Kusy 'wojtha' http://drupal.org/user/56154
-- Christian Schmidt 'c960657' http://drupal.org/user/216078
-- Damien Tournoud 'DamZ' http://drupal.org/user/22211
+- Vojtech Kusy 'wojtha' https://www.drupal.org/u/wojtha
+- Christian Schmidt 'c960657' https://www.drupal.org/u/c960657
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
 
 
 Overlay module
 Overlay module
-- Katherine Senzee 'ksenzee' http://drupal.org/user/139855
+- Katherine Senzee 'ksenzee' https://www.drupal.org/u/ksenzee
 
 
 Path module
 Path module
-- Dave Reid 'davereid' http://drupal.org/user/53892
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
 
 
 PHP module
 PHP module
 - ?
 - ?
 
 
 Poll module
 Poll module
-- Andrei Mateescu 'amateescu' http://drupal.org/user/729614
+- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
 
 
 Profile module
 Profile module
 - ?
 - ?
 
 
 RDF module
 RDF module
-- Stéphane Corlosquet 'scor' http://drupal.org/user/52142
+- Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor
 
 
 Search module
 Search module
-- Doug Green 'douggreen' http://drupal.org/user/29191
+- Doug Green 'douggreen' https://www.drupal.org/u/douggreen
 
 
 Shortcut module
 Shortcut module
-- David Rothstein 'David_Rothstein' http://drupal.org/user/124982
+- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
 
 
 Simpletest module
 Simpletest module
-- Jimmy Berry 'boombatower' http://drupal.org/user/214218
-- Károly Négyesi 'chx' http://drupal.org/user/9446
+- Jimmy Berry 'boombatower' https://www.drupal.org/u/boombatower
 
 
 Statistics module
 Statistics module
-- Tim Millwood 'timmillwood' http://drupal.org/user/227849
+- Tim Millwood 'timmillwood' https://www.drupal.org/u/timmillwood
 
 
 Syslog module
 Syslog module
-- Khalid Baheyeldin 'kbahey' http://drupal.org/user/4063
+- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
 
 
 System module
 System module
 - ?
 - ?
 
 
 Taxonomy module
 Taxonomy module
-- Jess Myrbo 'xjm' http://drupal.org/user/65776
-- Nathaniel Catchpole 'catch' http://drupal.org/user/35733
-- Benjamin Doherty 'bangpound' http://drupal.org/user/100456
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+- Benjamin Doherty 'bangpound' https://www.drupal.org/u/bangpound
 
 
 Toolbar module
 Toolbar module
 - ?
 - ?
 
 
 Tracker module
 Tracker module
-- David Strauss 'David Strauss' http://drupal.org/user/93254
+- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
 
 
 Translation module
 Translation module
-- Francesco Placella 'plach' http://drupal.org/user/183211
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
 
 
 Trigger module
 Trigger module
 - ?
 - ?
 
 
 Update module
 Update module
-- Derek Wright 'dww' http://drupal.org/user/46549
+- Derek Wright 'dww' https://www.drupal.org/u/dww
 
 
 User module
 User module
-- Moshe Weitzman 'moshe weitzman' http://drupal.org/user/23
-- David Strauss 'David Strauss' http://drupal.org/user/93254
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
 
 
 
 
 Theme maintainers
 Theme maintainers
 -----------------
 -----------------
 
 
 Bartik theme
 Bartik theme
-- Jen Simmons 'jensimmons' http://drupal.org/user/140882
-- Jeff Burns 'Jeff Burnz' http://drupal.org/user/61393
+- Jen Simmons 'jensimmons' https://www.drupal.org/u/jensimmons
+- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
 
 
 Garland theme
 Garland theme
-- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
 
 
 Seven theme
 Seven theme
-- Jeff Burns 'Jeff Burnz' http://drupal.org/user/61393
+- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
 
 
 Stark theme
 Stark theme
-- John Albin Wilkins 'JohnAlbin' http://drupal.org/user/32095
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin

+ 10 - 0
UPGRADE.txt

@@ -64,6 +64,9 @@ following the instructions in the INTRODUCTION section at the top of this file:
    Sometimes an update includes changes to default.settings.php (this will be
    Sometimes an update includes changes to default.settings.php (this will be
    noted in the release notes). If that's the case, follow these steps:
    noted in the release notes). If that's the case, follow these steps:
 
 
+   - Locate your settings.php file in the /sites/* directory. (Typically
+     sites/default.)
+
    - Make a backup copy of your settings.php file, with a different file name.
    - Make a backup copy of your settings.php file, with a different file name.
 
 
    - Make a copy of the new default.settings.php file, and name the copy
    - Make a copy of the new default.settings.php file, and name the copy
@@ -74,6 +77,13 @@ following the instructions in the INTRODUCTION section at the top of this file:
      database information, and you will also want to copy in any other
      database information, and you will also want to copy in any other
      customizations you have added.
      customizations you have added.
 
 
+   You can find the release notes for your version at
+   https://www.drupal.org/project/drupal. At bottom of the project page under
+   "Downloads" use the link for your version of Drupal to view the release
+   notes. If your version is not listed, use the 'View all releases' link. From
+   this page you can scroll down or use the filter to find your version and its
+   release notes.
+
 4. Download the latest Drupal 7.x release from http://drupal.org to a
 4. Download the latest Drupal 7.x release from http://drupal.org to a
    directory outside of your web root. Extract the archive and copy the files
    directory outside of your web root. Extract the archive and copy the files
    into your Drupal directory.
    into your Drupal directory.

+ 38 - 3
includes/ajax.inc

@@ -211,7 +211,7 @@
  *
  *
  * When returning an Ajax command array, it is often useful to have
  * When returning an Ajax command array, it is often useful to have
  * status messages rendered along with other tasks in the command array.
  * 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
  * @code
  *   $commands = array();
  *   $commands = array();
  *   $commands[] = ajax_command_replace(NULL, $output);
  *   $commands[] = ajax_command_replace(NULL, $output);
@@ -230,6 +230,10 @@
  *   functions.
  *   functions.
  */
  */
 function ajax_render($commands = array()) {
 function ajax_render($commands = array()) {
+  // Although ajax_deliver() does this, some contributed and custom modules
+  // render Ajax responses without using that delivery callback.
+  ajax_set_verification_header();
+
   // Ajax responses aren't rendered with html.tpl.php, so we have to call
   // Ajax responses aren't rendered with html.tpl.php, so we have to call
   // drupal_get_css() and drupal_get_js() here, in order to have new files added
   // drupal_get_css() and drupal_get_js() here, in order to have new files added
   // during this request to be loaded by the page. We only want to send back
   // during this request to be loaded by the page. We only want to send back
@@ -390,7 +394,7 @@ function ajax_form_callback() {
   if (!empty($form_state['triggering_element'])) {
   if (!empty($form_state['triggering_element'])) {
     $callback = $form_state['triggering_element']['#ajax']['callback'];
     $callback = $form_state['triggering_element']['#ajax']['callback'];
   }
   }
-  if (!empty($callback) && function_exists($callback)) {
+  if (!empty($callback) && is_callable($callback)) {
     $result = $callback($form, $form_state);
     $result = $callback($form, $form_state);
 
 
     if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
     if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
@@ -487,6 +491,9 @@ function ajax_deliver($page_callback_result) {
     }
     }
   }
   }
 
 
+  // Let ajax.js know that this response is safe to process.
+  ajax_set_verification_header();
+
   // Print the response.
   // Print the response.
   $commands = ajax_prepare_response($page_callback_result);
   $commands = ajax_prepare_response($page_callback_result);
   $json = ajax_render($commands);
   $json = ajax_render($commands);
@@ -576,6 +583,29 @@ function ajax_prepare_response($page_callback_result) {
   return $commands;
   return $commands;
 }
 }
 
 
+/**
+ * Sets a response header for ajax.js to trust the response body.
+ *
+ * It is not safe to invoke Ajax commands within user-uploaded files, so this
+ * header protects against those being invoked.
+ *
+ * @see Drupal.ajax.options.success()
+ */
+function ajax_set_verification_header() {
+  $added = &drupal_static(__FUNCTION__);
+
+  // User-uploaded files cannot set any response headers, so a custom header is
+  // used to indicate to ajax.js that this response is safe. Note that most
+  // Ajax requests bound using the Form API will be protected by having the URL
+  // flagged as trusted in Drupal.settings, so this header is used only for
+  // things like custom markup that gets Ajax behaviors attached.
+  if (empty($added)) {
+    drupal_add_http_header('X-Drupal-Ajax-Token', '1');
+    // Avoid sending the header twice.
+    $added = TRUE;
+  }
+}
+
 /**
 /**
  * Performs end-of-Ajax-request tasks.
  * Performs end-of-Ajax-request tasks.
  *
  *
@@ -764,7 +794,12 @@ function ajax_pre_render_element($element) {
 
 
     $element['#attached']['js'][] = array(
     $element['#attached']['js'][] = array(
       'type' => 'setting',
       'type' => 'setting',
-      'data' => array('ajax' => array($element['#id'] => $settings)),
+      'data' => array(
+        'ajax' => array($element['#id'] => $settings),
+        'urlIsAjaxTrusted' => array(
+          $settings['url'] => TRUE,
+        ),
+      ),
     );
     );
 
 
     // Indicate that Ajax processing was successful.
     // Indicate that Ajax processing was successful.

+ 2 - 2
includes/batch.inc

@@ -460,10 +460,10 @@ function _batch_finished() {
       if (isset($batch_set['file']) && is_file($batch_set['file'])) {
       if (isset($batch_set['file']) && is_file($batch_set['file'])) {
         include_once DRUPAL_ROOT . '/' . $batch_set['file'];
         include_once DRUPAL_ROOT . '/' . $batch_set['file'];
       }
       }
-      if (function_exists($batch_set['finished'])) {
+      if (is_callable($batch_set['finished'])) {
         $queue = _batch_queue($batch_set);
         $queue = _batch_queue($batch_set);
         $operations = $queue->getAllItems();
         $operations = $queue->getAllItems();
-        $batch_set['finished']($batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
+        call_user_func($batch_set['finished'], $batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
       }
       }
     }
     }
   }
   }

+ 411 - 80
includes/bootstrap.inc

@@ -8,7 +8,7 @@
 /**
 /**
  * The current system version.
  * The current system version.
  */
  */
-define('VERSION', '7.34');
+define('VERSION', '7.53');
 
 
 /**
 /**
  * Core API compatibility.
  * Core API compatibility.
@@ -529,9 +529,8 @@ function timer_stop($name) {
  * Returns the appropriate configuration directory.
  * Returns the appropriate configuration directory.
  *
  *
  * Returns the configuration path based on the site's hostname, port, and
  * 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
  * @param bool $require_settings
  *   Only configuration directories with an existing settings.php file
  *   Only configuration directories with an existing settings.php file
@@ -829,14 +828,21 @@ function drupal_settings_initialize() {
  * @param $filename
  * @param $filename
  *   The filename of the item if it is to be set explicitly rather
  *   The filename of the item if it is to be set explicitly rather
  *   than by consulting the database.
  *   than by consulting the database.
+ * @param bool $trigger_error
+ *   Whether to trigger an error when a file is missing or has unexpectedly
+ *   moved. This defaults to TRUE, but can be set to FALSE by calling code that
+ *   merely wants to check whether an item exists in the filesystem.
  *
  *
  * @return
  * @return
  *   The filename of the requested item or NULL if the item is not found.
  *   The filename of the requested item or NULL if the item is not found.
  */
  */
-function drupal_get_filename($type, $name, $filename = NULL) {
+function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TRUE) {
+  // The $files static variable will hold the locations of all requested files.
+  // We can be sure that any file listed in this static variable actually
+  // exists as all additions have gone through a file_exists() check.
   // The location of files will not change during the request, so do not use
   // The location of files will not change during the request, so do not use
   // drupal_static().
   // drupal_static().
-  static $files = array(), $dirs = array();
+  static $files = array();
 
 
   // Profiles are a special case: they have a fixed location and naming.
   // Profiles are a special case: they have a fixed location and naming.
   if ($type == 'profile') {
   if ($type == 'profile') {
@@ -848,64 +854,296 @@ function drupal_get_filename($type, $name, $filename = NULL) {
   }
   }
 
 
   if (!empty($filename) && file_exists($filename)) {
   if (!empty($filename) && file_exists($filename)) {
+    // Prime the static cache with the provided filename.
     $files[$type][$name] = $filename;
     $files[$type][$name] = $filename;
   }
   }
   elseif (isset($files[$type][$name])) {
   elseif (isset($files[$type][$name])) {
-    // nothing
+    // This item had already been found earlier in the request, either through
+    // priming of the static cache (for example, in system_list()), through a
+    // lookup in the {system} table, or through a file scan (cached or not). Do
+    // nothing.
   }
   }
-  // Verify that we have an active database connection, before querying
-  // the database. This is required because this function is called both
-  // before we have a database connection (i.e. during installation) and
-  // when a database connection fails.
   else {
   else {
+    // Look for the filename listed in the {system} table. Verify that we have
+    // an active database connection before doing so, since this function is
+    // called both before we have a database connection (i.e. during
+    // installation) and when a database connection fails.
+    $database_unavailable = TRUE;
     try {
     try {
       if (function_exists('db_query')) {
       if (function_exists('db_query')) {
         $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
         $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
         if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
         if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
           $files[$type][$name] = $file;
           $files[$type][$name] = $file;
         }
         }
+        $database_unavailable = FALSE;
       }
       }
     }
     }
     catch (Exception $e) {
     catch (Exception $e) {
       // The database table may not exist because Drupal is not yet installed,
       // The database table may not exist because Drupal is not yet installed,
-      // or the database might be down. We have a fallback for this case so we
-      // hide the error completely.
+      // the database might be down, or we may have done a non-database cache
+      // flush while $conf['page_cache_without_database'] = TRUE and
+      // $conf['page_cache_invoke_hooks'] = TRUE. We have a fallback for these
+      // cases so we hide the error completely.
     }
     }
-    // Fallback to searching the filesystem if the database could not find the
-    // file or the file returned by the database is not found.
+    // Fall back to searching the filesystem if the database could not find the
+    // file or the file does not exist at the path returned by the database.
     if (!isset($files[$type][$name])) {
     if (!isset($files[$type][$name])) {
-      // We have a consistent directory naming: modules, themes...
-      $dir = $type . 's';
-      if ($type == 'theme_engine') {
-        $dir = 'themes/engines';
-        $extension = 'engine';
-      }
-      elseif ($type == 'theme') {
-        $extension = 'info';
-      }
-      else {
-        $extension = $type;
-      }
+      $files[$type][$name] = _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable);
+    }
+  }
 
 
-      if (!isset($dirs[$dir][$extension])) {
-        $dirs[$dir][$extension] = TRUE;
-        if (!function_exists('drupal_system_listing')) {
-          require_once DRUPAL_ROOT . '/includes/common.inc';
-        }
-        // Scan the appropriate directories for all files with the requested
-        // extension, not just the file we are currently looking for. This
-        // prevents unnecessary scans from being repeated when this function is
-        // called more than once in the same page request.
-        $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
-        foreach ($matches as $matched_name => $file) {
-          $files[$type][$matched_name] = $file->uri;
+  if (isset($files[$type][$name])) {
+    return $files[$type][$name];
+  }
+}
+
+/**
+ * Performs a cached file system scan as a fallback when searching for a file.
+ *
+ * This function looks for the requested file by triggering a file scan,
+ * caching the new location if the file has moved and caching the miss
+ * if the file is missing. If a file had been marked as missing in a previous
+ * file scan, or if it has been marked as moved and is still in the last known
+ * location, no new file scan will be performed.
+ *
+ * @param string $type
+ *   The type of the item (theme, theme_engine, module, profile).
+ * @param string $name
+ *   The name of the item for which the filename is requested.
+ * @param bool $trigger_error
+ *   Whether to trigger an error when a file is missing or has unexpectedly
+ *   moved.
+ * @param bool $database_unavailable
+ *   Whether this function is being called because the Drupal database could
+ *   not be queried for the file's location.
+ *
+ * @return
+ *   The filename of the requested item or NULL if the item is not found.
+ *
+ * @see drupal_get_filename()
+ */
+function _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable) {
+  $file_scans = &_drupal_file_scan_cache();
+  $filename = NULL;
+
+  // If the cache indicates that the item is missing, or we can verify that the
+  // item exists in the location the cache says it exists in, use that.
+  if (isset($file_scans[$type][$name]) && ($file_scans[$type][$name] === FALSE || file_exists($file_scans[$type][$name]))) {
+    $filename = $file_scans[$type][$name];
+  }
+  // Otherwise, perform a new file scan to find the item.
+  else {
+    $filename = _drupal_get_filename_perform_file_scan($type, $name);
+    // Update the static cache, and mark the persistent cache for updating at
+    // the end of the page request. See drupal_file_scan_write_cache().
+    $file_scans[$type][$name] = $filename;
+    $file_scans['#write_cache'] = TRUE;
+  }
+
+  // If requested, trigger a user-level warning about the missing or
+  // unexpectedly moved file. If the database was unavailable, do not trigger a
+  // warning in the latter case, though, since if the {system} table could not
+  // be queried there is no way to know if the location found here was
+  // "unexpected" or not.
+  if ($trigger_error) {
+    $error_type = $filename === FALSE ? 'missing' : 'moved';
+    if ($error_type == 'missing' || !$database_unavailable) {
+      _drupal_get_filename_fallback_trigger_error($type, $name, $error_type);
+    }
+  }
+
+  // The cache stores FALSE for files that aren't found (to be able to
+  // distinguish them from files that have not yet been searched for), but
+  // drupal_get_filename() expects NULL for these instead, so convert to NULL
+  // before returning.
+  if ($filename === FALSE) {
+    $filename = NULL;
+  }
+  return $filename;
+}
+
+/**
+ * Returns the current list of cached file system scan results.
+ *
+ * @return
+ *   An associative array tracking the most recent file scan results for all
+ *   files that have had scans performed. The keys are the type and name of the
+ *   item that was searched for, and the values can be either:
+ *   - Boolean FALSE if the item was not found in the file system.
+ *   - A string pointing to the location where the item was found.
+ */
+function &_drupal_file_scan_cache() {
+  $file_scans = &drupal_static(__FUNCTION__, array());
+
+  // The file scan results are stored in a persistent cache (in addition to the
+  // static cache) but because this function can be called before the
+  // persistent cache is available, we must merge any items that were found
+  // earlier in the page request into the results from the persistent cache.
+  if (!isset($file_scans['#cache_merge_done'])) {
+    try {
+      if (function_exists('cache_get')) {
+        $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+        if (!empty($cache->data)) {
+          // File scan results from the current request should take precedence
+          // over the results from the persistent cache, since they are newer.
+          $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
         }
         }
+        // Set a flag to indicate that the persistent cache does not need to be
+        // merged again.
+        $file_scans['#cache_merge_done'] = TRUE;
       }
       }
     }
     }
+    catch (Exception $e) {
+      // Hide the error.
+    }
   }
   }
 
 
-  if (isset($files[$type][$name])) {
-    return $files[$type][$name];
+  return $file_scans;
+}
+
+/**
+ * Performs a file system scan to search for a system resource.
+ *
+ * @param $type
+ *   The type of the item (theme, theme_engine, module, profile).
+ * @param $name
+ *   The name of the item for which the filename is requested.
+ *
+ * @return
+ *   The filename of the requested item or FALSE if the item is not found.
+ *
+ * @see drupal_get_filename()
+ * @see _drupal_get_filename_fallback()
+ */
+function _drupal_get_filename_perform_file_scan($type, $name) {
+  // The location of files will not change during the request, so do not use
+  // drupal_static().
+  static $dirs = array(), $files = array();
+
+  // We have a consistent directory naming: modules, themes...
+  $dir = $type . 's';
+  if ($type == 'theme_engine') {
+    $dir = 'themes/engines';
+    $extension = 'engine';
+  }
+  elseif ($type == 'theme') {
+    $extension = 'info';
+  }
+  else {
+    $extension = $type;
+  }
+
+  // Check if we had already scanned this directory/extension combination.
+  if (!isset($dirs[$dir][$extension])) {
+    // Log that we have now scanned this directory/extension combination
+    // into a static variable so as to prevent unnecessary file scans.
+    $dirs[$dir][$extension] = TRUE;
+    if (!function_exists('drupal_system_listing')) {
+      require_once DRUPAL_ROOT . '/includes/common.inc';
+    }
+    // Scan the appropriate directories for all files with the requested
+    // extension, not just the file we are currently looking for. This
+    // prevents unnecessary scans from being repeated when this function is
+    // called more than once in the same page request.
+    $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
+    foreach ($matches as $matched_name => $file) {
+      // Log the locations found in the file scan into a static variable.
+      $files[$type][$matched_name] = $file->uri;
+    }
+  }
+
+  // Return the results of the file system scan, or FALSE to indicate the file
+  // was not found.
+  return isset($files[$type][$name]) ? $files[$type][$name] : FALSE;
+}
+
+/**
+ * Triggers a user-level warning for missing or unexpectedly moved files.
+ *
+ * @param $type
+ *   The type of the item (theme, theme_engine, module, profile).
+ * @param $name
+ *   The name of the item for which the filename is requested.
+ * @param $error_type
+ *   The type of the error ('missing' or 'moved').
+ *
+ * @see drupal_get_filename()
+ * @see _drupal_get_filename_fallback()
+ */
+function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type) {
+  // Hide messages due to known bugs that will appear on a lot of sites.
+  // @todo Remove this in https://www.drupal.org/node/2383823
+  if (empty($name)) {
+    return;
+  }
+
+  // Make sure we only show any missing or moved file errors only once per
+  // request.
+  static $errors_triggered = array();
+  if (empty($errors_triggered[$type][$name][$error_type])) {
+    // Use _drupal_trigger_error_with_delayed_logging() here since these are
+    // triggered during low-level operations that cannot necessarily be
+    // interrupted by a watchdog() call.
+    if ($error_type == 'missing') {
+      _drupal_trigger_error_with_delayed_logging(format_string('The following @type is missing from the file system: %name. For information about how to fix this, see <a href="@documentation">the documentation page</a>.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+    }
+    elseif ($error_type == 'moved') {
+      _drupal_trigger_error_with_delayed_logging(format_string('The following @type has moved within the file system: %name. In order to fix this, clear caches or put the @type back in its original location. For more information, see <a href="@documentation">the documentation page</a>.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+    }
+    $errors_triggered[$type][$name][$error_type] = TRUE;
+  }
+}
+
+/**
+ * Invokes trigger_error() with logging delayed until the end of the request.
+ *
+ * This is an alternative to PHP's trigger_error() function which can be used
+ * during low-level Drupal core operations that need to avoid being interrupted
+ * by a watchdog() call.
+ *
+ * Normally, Drupal's error handler calls watchdog() in response to a
+ * trigger_error() call. However, this invokes hook_watchdog() which can run
+ * arbitrary code. If the trigger_error() happens in the middle of an
+ * operation such as a rebuild operation which should not be interrupted by
+ * arbitrary code, that could potentially break or trigger the rebuild again.
+ * This function protects against that by delaying the watchdog() call until
+ * the end of the current page request.
+ *
+ * This is an internal function which should only be called by low-level Drupal
+ * core functions. It may be removed in a future Drupal 7 release.
+ *
+ * @param string $error_msg
+ *   The error message to trigger. As with trigger_error() itself, this is
+ *   limited to 1024 bytes; additional characters beyond that will be removed.
+ * @param int $error_type
+ *   (optional) The type of error. This should be one of the E_USER family of
+ *   constants. As with trigger_error() itself, this defaults to E_USER_NOTICE
+ *   if not provided.
+ *
+ * @see _drupal_log_error()
+ */
+function _drupal_trigger_error_with_delayed_logging($error_msg, $error_type = E_USER_NOTICE) {
+  $delay_logging = &drupal_static(__FUNCTION__, FALSE);
+  $delay_logging = TRUE;
+  trigger_error($error_msg, $error_type);
+  $delay_logging = FALSE;
+}
+
+/**
+ * Writes the file scan cache to the persistent cache.
+ *
+ * This cache stores all files marked as missing or moved after a file scan
+ * to prevent unnecessary file scans in subsequent requests. This cache is
+ * cleared in system_list_reset() (i.e. after a module/theme rebuild).
+ */
+function drupal_file_scan_write_cache() {
+  // Only write to the persistent cache if requested, and if we know that any
+  // data previously in the cache was successfully loaded and merged in by
+  // _drupal_file_scan_cache().
+  $file_scans = &_drupal_file_scan_cache();
+  if (isset($file_scans['#write_cache']) && isset($file_scans['#cache_merge_done'])) {
+    unset($file_scans['#write_cache']);
+    cache_set('_drupal_file_scan_cache', $file_scans, 'cache_bootstrap');
   }
   }
 }
 }
 
 
@@ -1056,7 +1294,7 @@ function drupal_page_get_cache($check_only = FALSE) {
  * Determines the cacheability of the current page.
  * Determines the cacheability of the current page.
  *
  *
  * @param $allow_caching
  * @param $allow_caching
- *   Set to FALSE if you want to prevent this page to get cached.
+ *   Set to FALSE if you want to prevent this page from being cached.
  *
  *
  * @return
  * @return
  *   TRUE if the current page can be cached, FALSE otherwise.
  *   TRUE if the current page can be cached, FALSE otherwise.
@@ -1246,23 +1484,10 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
  * fresh page on every request. This prevents authenticated users from seeing
  * fresh page on every request. This prevents authenticated users from seeing
  * locally cached pages.
  * 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()
  * @see drupal_page_set_cache()
  */
  */
@@ -1275,9 +1500,11 @@ function drupal_page_header() {
 
 
   $default_headers = array(
   $default_headers = array(
     'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
     'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
-    'Last-Modified' => gmdate(DATE_RFC7231, REQUEST_TIME),
-    'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
-    'ETag' => '"' . REQUEST_TIME . '"',
+    'Cache-Control' => 'no-cache, must-revalidate',
+    // Prevent browsers from sniffing a response and picking a MIME type
+    // different from the declared content-type, since that can lead to
+    // XSS and other vulnerabilities.
+    'X-Content-Type-Options' => 'nosniff',
   );
   );
   drupal_send_headers($default_headers);
   drupal_send_headers($default_headers);
 }
 }
@@ -1451,6 +1678,23 @@ function drupal_unpack($obj, $field = 'data') {
  * available to code that needs localization. See st() and get_t() for
  * available to code that needs localization. See st() and get_t() for
  * alternatives.
  * alternatives.
  *
  *
+ * @section sec_context String context
+ * Matching source strings are normally only translated once, and the same
+ * translation is used everywhere that has a matching string. However, in some
+ * cases, a certain English source string needs to have multiple translations.
+ * One example of this is the string "May", which could be used as either a
+ * full month name or a 3-letter abbreviated month. In other languages where
+ * the month name for May has more than 3 letters, you would need to provide
+ * two different translations (one for the full name and one abbreviated), and
+ * the correct form would need to be chosen, depending on how "May" is being
+ * used. To facilitate this, the "May" string should be provided with two
+ * different contexts in the $options parameter when calling t(). For example:
+ * @code
+ * t('May', array(), array('context' => 'Long month name')
+ * t('May', array(), array('context' => 'Abbreviated month name')
+ * @endcode
+ * See https://localize.drupal.org/node/2109 for more information.
+ *
  * @param $string
  * @param $string
  *   A string containing the English string to translate.
  *   A string containing the English string to translate.
  * @param $args
  * @param $args
@@ -1461,8 +1705,9 @@ function drupal_unpack($obj, $field = 'data') {
  *   An associative array of additional options, with the following elements:
  *   An associative array of additional options, with the following elements:
  *   - 'langcode' (defaults to the current language): The language code to
  *   - 'langcode' (defaults to the current language): The language code to
  *     translate to a language other than what is used to display the page.
  *     translate to a language other than what is used to display the page.
- *   - 'context' (defaults to the empty context): The context the source string
- *     belongs to.
+ *   - 'context' (defaults to the empty context): A string giving the context
+ *     that the source string belongs to. See @ref sec_context above for more
+ *     information.
  *
  *
  * @return
  * @return
  *   The translated string.
  *   The translated string.
@@ -1659,14 +1904,14 @@ function request_uri() {
  *   information about the passed-in exception is used.
  *   information about the passed-in exception is used.
  * @param $variables
  * @param $variables
  *   Array of variables to replace in the message on display. Defaults to the
  *   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
  * @param $severity
  *   The severity of the message, as per RFC 3164.
  *   The severity of the message, as per RFC 3164.
  * @param $link
  * @param $link
  *   A link to associate with the message.
  *   A link to associate with the message.
  *
  *
  * @see watchdog()
  * @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) {
 function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {
 
 
@@ -1792,7 +2037,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
  * @see theme_status_messages()
  * @see theme_status_messages()
  */
  */
 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
-  if ($message) {
+  if ($message || $message === '0' || $message === 0) {
     if (!isset($_SESSION['messages'][$type])) {
     if (!isset($_SESSION['messages'][$type])) {
       $_SESSION['messages'][$type] = array();
       $_SESSION['messages'][$type] = array();
     }
     }
@@ -2480,6 +2725,9 @@ function _drupal_bootstrap_database() {
   // the install or upgrade process.
   // the install or upgrade process.
   spl_autoload_register('drupal_autoload_class');
   spl_autoload_register('drupal_autoload_class');
   spl_autoload_register('drupal_autoload_interface');
   spl_autoload_register('drupal_autoload_interface');
+  if (version_compare(PHP_VERSION, '5.4') >= 0) {
+    spl_autoload_register('drupal_autoload_trait');
+  }
 }
 }
 
 
 /**
 /**
@@ -2497,6 +2745,26 @@ function _drupal_bootstrap_variables() {
   // Load bootstrap modules.
   // Load bootstrap modules.
   require_once DRUPAL_ROOT . '/includes/module.inc';
   require_once DRUPAL_ROOT . '/includes/module.inc';
   module_load_all(TRUE);
   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']);
+    }
+  }
 }
 }
 
 
 /**
 /**
@@ -2633,7 +2901,7 @@ function drupal_installation_attempted() {
  *
  *
  * This would include implementations of hook_install(), which could run
  * This would include implementations of hook_install(), which could run
  * during the Drupal installation phase, and might also be run during
  * 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.
  * module administration page.
  *
  *
  * Example usage:
  * Example usage:
@@ -2775,10 +3043,14 @@ function language_list($field = 'language') {
 }
 }
 
 
 /**
 /**
- * Returns the default language used on the site
+ * Returns the default language, as an object, or one of its properties.
  *
  *
  * @param $property
  * @param $property
- *   Optional property of the language object to return
+ *   (optional) The property of the language object to return.
+ *
+ * @return
+ *   Either the language object for the default language used on the site,
+ *   or the property of that object named in the $property parameter.
  */
  */
 function language_default($property = NULL) {
 function language_default($property = NULL) {
   $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
   $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
@@ -2930,8 +3202,15 @@ function ip_address() {
         // Eliminate all trusted IPs.
         // Eliminate all trusted IPs.
         $untrusted = array_diff($forwarded, $reverse_proxy_addresses);
         $untrusted = array_diff($forwarded, $reverse_proxy_addresses);
 
 
-        // The right-most IP is the most specific we can trust.
-        $ip_address = array_pop($untrusted);
+        if (!empty($untrusted)) {
+          // The right-most IP is the most specific we can trust.
+          $ip_address = array_pop($untrusted);
+        }
+        else {
+          // All IP addresses in the forwarded array are configured proxy IPs
+          // (and thus trusted). We take the leftmost IP.
+          $ip_address = array_shift($forwarded);
+        }
       }
       }
     }
     }
   }
   }
@@ -2948,7 +3227,9 @@ function ip_address() {
  * Gets the schema definition of a table, or the whole database schema.
  * Gets the schema definition of a table, or the whole database schema.
  *
  *
  * The returned schema will include any modifications made by any
  * The returned schema will include any modifications made by any
- * module that implements hook_schema_alter().
+ * module that implements hook_schema_alter(). To get the schema without
+ * modifications, use drupal_get_schema_unprocessed().
+ *
  *
  *
  * @param $table
  * @param $table
  *   The name of the table. If not given, the schema of all tables is returned.
  *   The name of the table. If not given, the schema of all tables is returned.
@@ -3103,6 +3384,22 @@ function drupal_autoload_class($class) {
   return _registry_check_code('class', $class);
   return _registry_check_code('class', $class);
 }
 }
 
 
+/**
+ * Confirms that a trait is available.
+ *
+ * This function is rarely called directly. Instead, it is registered as an
+ * spl_autoload() handler, and PHP calls it for us when necessary.
+ *
+ * @param string $trait
+ *   The name of the trait to check or load.
+ *
+ * @return bool
+ *   TRUE if the trait is currently available, FALSE otherwise.
+ */
+function drupal_autoload_trait($trait) {
+  return _registry_check_code('trait', $trait);
+}
+
 /**
 /**
  * Checks for a resource in the registry.
  * Checks for a resource in the registry.
  *
  *
@@ -3121,7 +3418,7 @@ function drupal_autoload_class($class) {
 function _registry_check_code($type, $name = NULL) {
 function _registry_check_code($type, $name = NULL) {
   static $lookup_cache, $cache_update_needed;
   static $lookup_cache, $cache_update_needed;
 
 
-  if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name)) {
+  if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name) || $type == 'trait' && trait_exists($name)) {
     return TRUE;
     return TRUE;
   }
   }
 
 
@@ -3154,7 +3451,7 @@ function _registry_check_code($type, $name = NULL) {
   $cache_key = $type[0] . $name;
   $cache_key = $type[0] . $name;
   if (isset($lookup_cache[$cache_key])) {
   if (isset($lookup_cache[$cache_key])) {
     if ($lookup_cache[$cache_key]) {
     if ($lookup_cache[$cache_key]) {
-      require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
+      include_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
     }
     }
     return (bool) $lookup_cache[$cache_key];
     return (bool) $lookup_cache[$cache_key];
   }
   }
@@ -3162,10 +3459,13 @@ function _registry_check_code($type, $name = NULL) {
   // This function may get called when the default database is not active, but
   // 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
   // there is no reason we'd ever want to not use the default database for
   // this query.
   // 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();
     ->fetchField();
 
 
   // Flag that we've run a lookup query and need to update the cache.
   // Flag that we've run a lookup query and need to update the cache.
@@ -3176,7 +3476,7 @@ function _registry_check_code($type, $name = NULL) {
   $lookup_cache[$cache_key] = $file;
   $lookup_cache[$cache_key] = $file;
 
 
   if ($file) {
   if ($file) {
-    require_once DRUPAL_ROOT . '/' . $file;
+    include_once DRUPAL_ROOT . '/' . $file;
     return TRUE;
     return TRUE;
   }
   }
   else {
   else {
@@ -3503,3 +3803,34 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) {
   // - The memory limit is greater than the memory required for the operation.
   // - 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)));
   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);
+  }
+}

+ 1 - 0
includes/cache.inc

@@ -14,6 +14,7 @@
  *
  *
  * @param $bin
  * @param $bin
  *   The cache bin for which the cache object should be returned.
  *   The cache bin for which the cache object should be returned.
+ *
  * @return DrupalCacheInterface
  * @return DrupalCacheInterface
  *   The cache object associated with the specified bin.
  *   The cache object associated with the specified bin.
  *
  *

+ 200 - 44
includes/common.inc

@@ -688,6 +688,13 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
     $options['fragment'] = $destination['fragment'];
     $options['fragment'] = $destination['fragment'];
   }
   }
 
 
+  // In some cases modules call drupal_goto(current_path()). We need to ensure
+  // that such a redirect is not to an external URL.
+  if ($path === current_path() && empty($options['external']) && url_is_external($path)) {
+    // Force url() to generate a non-external URL.
+    $options['external'] = FALSE;
+  }
+
   drupal_alter('drupal_goto', $path, $options, $http_response_code);
   drupal_alter('drupal_goto', $path, $options, $http_response_code);
 
 
   // The 'Location' HTTP header must be absolute.
   // The 'Location' HTTP header must be absolute.
@@ -753,7 +760,8 @@ function drupal_access_denied() {
  *   - headers: An array containing request headers to send as name/value pairs.
  *   - headers: An array containing request headers to send as name/value pairs.
  *   - method: A string containing the request method. Defaults to 'GET'.
  *   - method: A string containing the request method. Defaults to 'GET'.
  *   - data: A string containing the request body, formatted as
  *   - data: A string containing the request body, formatted as
- *     'param=value&param=value&...'. Defaults to NULL.
+ *     'param=value&param=value&...'; to generate this, use http_build_query().
+ *     Defaults to NULL.
  *   - max_redirects: An integer representing how many times a redirect
  *   - max_redirects: An integer representing how many times a redirect
  *     may be followed. Defaults to 3.
  *     may be followed. Defaults to 3.
  *   - timeout: A float representing the maximum number of seconds the function
  *   - timeout: A float representing the maximum number of seconds the function
@@ -778,6 +786,8 @@ function drupal_access_denied() {
  *     HTTP header names are case-insensitive (RFC 2616, section 4.2), so for
  *     HTTP header names are case-insensitive (RFC 2616, section 4.2), so for
  *     easy access the array keys are returned in lower case.
  *     easy access the array keys are returned in lower case.
  *   - data: A string containing the response body that was received.
  *   - data: A string containing the response body that was received.
+ *
+ * @see http_build_query()
  */
  */
 function drupal_http_request($url, array $options = array()) {
 function drupal_http_request($url, array $options = array()) {
   // Allow an alternate HTTP client library to replace Drupal's default
   // Allow an alternate HTTP client library to replace Drupal's default
@@ -1057,6 +1067,12 @@ function drupal_http_request($url, array $options = array()) {
 
 
   switch ($code) {
   switch ($code) {
     case 200: // OK
     case 200: // OK
+    case 201: // Created
+    case 202: // Accepted
+    case 203: // Non-Authoritative Information
+    case 204: // No Content
+    case 205: // Reset Content
+    case 206: // Partial Content
     case 304: // Not modified
     case 304: // Not modified
       break;
       break;
     case 301: // Moved permanently
     case 301: // Moved permanently
@@ -1522,7 +1538,7 @@ function _filter_xss_split($m, $store = FALSE) {
     return '&lt;';
     return '&lt;';
   }
   }
 
 
-  if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
+  if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
     // Seriously malformed.
     // Seriously malformed.
     return '';
     return '';
   }
   }
@@ -1754,9 +1770,15 @@ function format_rss_item($title, $link, $description, $args = array()) {
  *     - 'key': element name
  *     - 'key': element name
  *     - 'value': element contents
  *     - 'value': element contents
  *     - 'attributes': associative array of element attributes
  *     - 'attributes': associative array of element attributes
+ *     - 'encoded': TRUE if 'value' is already encoded
  *
  *
  * In both cases, 'value' can be a simple string, or it can be another array
  * In both cases, 'value' can be a simple string, or it can be another array
  * with the same format as $array itself for nesting.
  * with the same format as $array itself for nesting.
+ *
+ * If 'encoded' is TRUE it is up to the caller to ensure that 'value' is either
+ * entity-encoded or CDATA-escaped. Using this option is not recommended when
+ * working with untrusted user input, since failing to escape the data
+ * correctly has security implications.
  */
  */
 function format_xml_elements($array) {
 function format_xml_elements($array) {
   $output = '';
   $output = '';
@@ -1769,7 +1791,7 @@ function format_xml_elements($array) {
         }
         }
 
 
         if (isset($value['value']) && $value['value'] != '') {
         if (isset($value['value']) && $value['value'] != '') {
-          $output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) . '</' . $value['key'] . ">\n";
+          $output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : (!empty($value['encoded']) ? $value['value'] : check_plain($value['value']))) . '</' . $value['key'] . ">\n";
         }
         }
         else {
         else {
           $output .= " />\n";
           $output .= " />\n";
@@ -2215,13 +2237,7 @@ function url($path = NULL, array $options = array()) {
   );
   );
 
 
   if (!isset($options['external'])) {
   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.
-    $colonpos = strpos($path, ':');
-    $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
+    $options['external'] = url_is_external($path);
   }
   }
 
 
   // Preserve the original path before altering or aliasing.
   // Preserve the original path before altering or aliasing.
@@ -2259,6 +2275,11 @@ function url($path = NULL, array $options = array()) {
     return $path . $options['fragment'];
     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;
   global $base_url, $base_secure_url, $base_insecure_url;
 
 
   // The base_url might be rewritten from the language rewrite in domain mode.
   // The base_url might be rewritten from the language rewrite in domain mode.
@@ -2336,10 +2357,21 @@ function url($path = NULL, array $options = array()) {
  */
  */
 function url_is_external($path) {
 function url_is_external($path) {
   $colonpos = strpos($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;
+  // Some browsers treat \ as / so normalize to forward slashes.
+  $path = str_replace('\\', '/', $path);
+  // If the path starts with 2 slashes then it is always considered an external
+  // URL without an explicit protocol part.
+  return (strpos($path, '//') === 0)
+    // Leading control characters may be ignored or mishandled by browsers, so
+    // assume such a path may lead to an external location. The \p{C} character
+    // class matches all UTF-8 control, unassigned, and private characters.
+    || (preg_match('/^\p{C}/u', $path) !== 0)
+    // 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.
+    || ($colonpos !== FALSE
+      && !preg_match('![/?#]!', substr($path, 0, $colonpos))
+      && drupal_strip_dangerous_protocols($path) == $path);
 }
 }
 
 
 /**
 /**
@@ -2621,6 +2653,15 @@ function drupal_deliver_html_page($page_callback_result) {
   global $language;
   global $language;
   drupal_add_http_header('Content-Language', $language->language);
   drupal_add_http_header('Content-Language', $language->language);
 
 
+  // By default, do not allow the site to be rendered in an iframe on another
+  // domain, but provide a variable to override this. If the code running for
+  // this page request already set the X-Frame-Options header earlier, don't
+  // overwrite it here.
+  $frame_options = variable_get('x_frame_options', 'SAMEORIGIN');
+  if ($frame_options && is_null(drupal_get_http_header('X-Frame-Options'))) {
+    drupal_add_http_header('X-Frame-Options', $frame_options);
+  }
+
   // Menu status constants are integers; page content is a string or array.
   // Menu status constants are integers; page content is a string or array.
   if (is_int($page_callback_result)) {
   if (is_int($page_callback_result)) {
     // @todo: Break these up into separate functions?
     // @todo: Break these up into separate functions?
@@ -2636,7 +2677,10 @@ function drupal_deliver_html_page($page_callback_result) {
 
 
         // Keep old path for reference, and to allow forms to redirect to it.
         // Keep old path for reference, and to allow forms to redirect to it.
         if (!isset($_GET['destination'])) {
         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', ''));
         $path = drupal_get_normal_path(variable_get('site_404', ''));
@@ -2665,7 +2709,10 @@ function drupal_deliver_html_page($page_callback_result) {
 
 
         // Keep old path for reference, and to allow forms to redirect to it.
         // Keep old path for reference, and to allow forms to redirect to it.
         if (!isset($_GET['destination'])) {
         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', ''));
         $path = drupal_get_normal_path(variable_get('site_403', ''));
@@ -2729,6 +2776,7 @@ function drupal_page_footer() {
   _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
   _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
   drupal_cache_system_paths();
   drupal_cache_system_paths();
   module_implements_write_cache();
   module_implements_write_cache();
+  drupal_file_scan_write_cache();
   system_run_automated_cron();
   system_run_automated_cron();
 }
 }
 
 
@@ -2790,11 +2838,11 @@ function drupal_map_assoc($array, $function = NULL) {
  * into script execution a call such as set_time_limit(20) is made, the
  * into script execution a call such as set_time_limit(20) is made, the
  * script will run for a total of 45 seconds before timing out.
  * script will run for a total of 45 seconds before timing out.
  *
  *
- * It also means that it is possible to decrease the total time limit if
- * the sum of the new time limit and the current time spent running the
- * script is inferior to the original time limit. It is inherent to the way
- * set_time_limit() works, it should rather be called with an appropriate
- * value every time you need to allocate a certain amount of time
+ * If the current time limit is not unlimited it is possible to decrease the
+ * total time limit if the sum of the new time limit and the current time spent
+ * running the script is inferior to the original time limit. It is inherent to
+ * the way set_time_limit() works, it should rather be called with an
+ * appropriate value every time you need to allocate a certain amount of time
  * to execute a task than only once at the beginning of the script.
  * to execute a task than only once at the beginning of the script.
  *
  *
  * Before calling set_time_limit(), we check if this function is available
  * Before calling set_time_limit(), we check if this function is available
@@ -2811,7 +2859,11 @@ function drupal_map_assoc($array, $function = NULL) {
  */
  */
 function drupal_set_time_limit($time_limit) {
 function drupal_set_time_limit($time_limit) {
   if (function_exists('set_time_limit')) {
   if (function_exists('set_time_limit')) {
-    @set_time_limit($time_limit);
+    $current = ini_get('max_execution_time');
+    // Do not set time limit if it is currently unlimited.
+    if ($current != 0) {
+      @set_time_limit($time_limit);
+    }
   }
   }
 }
 }
 
 
@@ -2992,6 +3044,13 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
  */
  */
 function drupal_add_css($data = NULL, $options = NULL) {
 function drupal_add_css($data = NULL, $options = NULL) {
   $css = &drupal_static(__FUNCTION__, array());
   $css = &drupal_static(__FUNCTION__, array());
+  $count = &drupal_static(__FUNCTION__ . '_count', 0);
+
+  // If the $css variable has been reset with drupal_static_reset(), there is
+  // no longer any CSS being tracked, so set the counter back to 0 also.
+  if (count($css) === 0) {
+    $count = 0;
+  }
 
 
   // Construct the options, taking the defaults into consideration.
   // Construct the options, taking the defaults into consideration.
   if (isset($options)) {
   if (isset($options)) {
@@ -3027,7 +3086,8 @@ function drupal_add_css($data = NULL, $options = NULL) {
     }
     }
 
 
     // Always add a tiny value to the weight, to conserve the insertion order.
     // Always add a tiny value to the weight, to conserve the insertion order.
-    $options['weight'] += count($css) / 1000;
+    $options['weight'] += $count / 1000;
+    $count++;
 
 
     // Add the data to the CSS array depending on the type.
     // Add the data to the CSS array depending on the type.
     switch ($options['type']) {
     switch ($options['type']) {
@@ -3780,7 +3840,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
 
 
   // Replaces @import commands with the actual stylesheet content.
   // Replaces @import commands with the actual stylesheet content.
   // This happens recursively but omits external files.
   // This happens recursively but omits external files.
-  $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
+  $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
   return $contents;
   return $contents;
 }
 }
 
 
@@ -3840,6 +3900,21 @@ function drupal_delete_file_if_stale($uri) {
  *   The cleaned identifier.
  *   The cleaned identifier.
  */
  */
 function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) {
 function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) {
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['allow_css_double_underscores'] = &drupal_static(__FUNCTION__ . ':allow_css_double_underscores');
+  }
+  $allow_css_double_underscores = &$drupal_static_fast['allow_css_double_underscores'];
+  if (!isset($allow_css_double_underscores)) {
+    $allow_css_double_underscores = variable_get('allow_css_double_underscores', FALSE);
+  }
+
+  // Preserve BEM-style double-underscores depending on custom setting.
+  if ($allow_css_double_underscores) {
+    $filter['__'] = '__';
+  }
+
   // By default, we filter using Drupal's coding standards.
   // By default, we filter using Drupal's coding standards.
   $identifier = strtr($identifier, $filter);
   $identifier = strtr($identifier, $filter);
 
 
@@ -4140,6 +4215,13 @@ function drupal_region_class($region) {
  *       else being the same, JavaScript added by a call to drupal_add_js() that
  *       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
  *       happened later in the page request gets added to the page after one for
  *       which drupal_add_js() happened earlier in the page request.
  *       which drupal_add_js() happened earlier in the page request.
+ *   - 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 <script>
  *   - defer: If set to TRUE, the defer attribute is set on the <script>
  *     tag. Defaults to FALSE.
  *     tag. Defaults to FALSE.
  *   - cache: If set to FALSE, the JavaScript file is loaded anew on every page
  *   - cache: If set to FALSE, the JavaScript file is loaded anew on every page
@@ -4157,6 +4239,14 @@ function drupal_region_class($region) {
  */
  */
 function drupal_add_js($data = NULL, $options = NULL) {
 function drupal_add_js($data = NULL, $options = NULL) {
   $javascript = &drupal_static(__FUNCTION__, array());
   $javascript = &drupal_static(__FUNCTION__, array());
+  $jquery_added = &drupal_static(__FUNCTION__ . ':jquery_added', FALSE);
+
+  // If the $javascript variable has been reset with drupal_static_reset(),
+  // jQuery and related files will have been removed from the list, so set the
+  // variable back to FALSE to indicate they have not yet been added.
+  if (empty($javascript)) {
+    $jquery_added = FALSE;
+  }
 
 
   // Construct the options, taking the defaults into consideration.
   // Construct the options, taking the defaults into consideration.
   if (isset($options)) {
   if (isset($options)) {
@@ -4167,6 +4257,9 @@ function drupal_add_js($data = NULL, $options = NULL) {
   else {
   else {
     $options = array();
     $options = array();
   }
   }
+  if (isset($options['type']) && $options['type'] == 'setting') {
+    $options += array('requires_jquery' => FALSE);
+  }
   $options += drupal_js_defaults($data);
   $options += drupal_js_defaults($data);
 
 
   // Preprocess can only be set if caching is enabled.
   // Preprocess can only be set if caching is enabled.
@@ -4177,14 +4270,18 @@ function drupal_add_js($data = NULL, $options = NULL) {
   $options['weight'] += count($javascript) / 1000;
   $options['weight'] += count($javascript) / 1000;
 
 
   if (isset($data)) {
   if (isset($data)) {
-    // Add jquery.js and drupal.js, as well as the basePath setting, the
-    // first time a JavaScript file is added.
-    if (empty($javascript)) {
+    // Add jquery.js, drupal.js, and related files and settings if they have
+    // not been added yet. However, if the 'javascript_always_use_jquery'
+    // variable is set to FALSE (indicating that the site does not want jQuery
+    // automatically added on all pages) then only add it if a file or setting
+    // that requires jQuery is being added also.
+    if (!$jquery_added && (variable_get('javascript_always_use_jquery', TRUE) || $options['requires_jquery'])) {
+      $jquery_added = TRUE;
       // url() generates the prefix using hook_url_outbound_alter(). Instead of
       // url() generates the prefix using hook_url_outbound_alter(). Instead of
       // running the hook_url_outbound_alter() again here, extract the prefix
       // running the hook_url_outbound_alter() again here, extract the prefix
       // from url().
       // from url().
       url('', array('prefix' => &$prefix));
       url('', array('prefix' => &$prefix));
-      $javascript = array(
+      $default_javascript = array(
         'settings' => array(
         'settings' => array(
           'data' => array(
           'data' => array(
             array('basePath' => base_path()),
             array('basePath' => base_path()),
@@ -4203,11 +4300,13 @@ function drupal_add_js($data = NULL, $options = NULL) {
           'group' => JS_LIBRARY,
           'group' => JS_LIBRARY,
           'every_page' => TRUE,
           'every_page' => TRUE,
           'weight' => -1,
           'weight' => -1,
+          'requires_jquery' => TRUE,
           'preprocess' => TRUE,
           'preprocess' => TRUE,
           'cache' => TRUE,
           'cache' => TRUE,
           'defer' => FALSE,
           'defer' => FALSE,
         ),
         ),
       );
       );
+      $javascript = drupal_array_merge_deep($javascript, $default_javascript);
       // Register all required libraries.
       // Register all required libraries.
       drupal_add_library('system', 'jquery', TRUE);
       drupal_add_library('system', 'jquery', TRUE);
       drupal_add_library('system', 'jquery.once', TRUE);
       drupal_add_library('system', 'jquery.once', TRUE);
@@ -4248,6 +4347,7 @@ function drupal_js_defaults($data = NULL) {
     'group' => JS_DEFAULT,
     'group' => JS_DEFAULT,
     'every_page' => FALSE,
     'every_page' => FALSE,
     'weight' => 0,
     'weight' => 0,
+    'requires_jquery' => TRUE,
     'scope' => 'header',
     'scope' => 'header',
     'cache' => TRUE,
     'cache' => TRUE,
     'defer' => FALSE,
     'defer' => FALSE,
@@ -4294,7 +4394,12 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
   if (!isset($javascript)) {
   if (!isset($javascript)) {
     $javascript = drupal_add_js();
     $javascript = drupal_add_js();
   }
   }
-  if (empty($javascript)) {
+
+  // If no JavaScript items have been added, or if the only JavaScript items
+  // that have been added are JavaScript settings (which don't do anything
+  // without any JavaScript code to use them), then no JavaScript code should
+  // be added to the page.
+  if (empty($javascript) || (isset($javascript['settings']) && count($javascript) == 1)) {
     return '';
     return '';
   }
   }
 
 
@@ -4448,8 +4553,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
  *
  *
  * Libraries, JavaScript, CSS and other types of custom structures are attached
  * Libraries, JavaScript, CSS and other types of custom structures are attached
  * to elements using the #attached property. The #attached property is an
  * to elements using the #attached property. The #attached property is an
- * associative array, where the keys are the the attachment types and the values
- * are the attached data. For example:
+ * associative array, where the keys are the attachment types and the values are
+ * the attached data. For example:
  * @code
  * @code
  * $build['#attached'] = array(
  * $build['#attached'] = array(
  *   'js' => array(drupal_get_path('module', 'taxonomy') . '/taxonomy.js'),
  *   'js' => array(drupal_get_path('module', 'taxonomy') . '/taxonomy.js'),
@@ -5160,6 +5265,11 @@ function _drupal_bootstrap_full() {
   fix_gpc_magic();
   fix_gpc_magic();
   // Load all enabled modules
   // Load all enabled modules
   module_load_all();
   module_load_all();
+  // Reset drupal_alter() and module_implements() static caches as these
+  // include implementations for vital modules only when called early on
+  // in the bootstrap.
+  drupal_static_reset('drupal_alter');
+  drupal_static_reset('module_implements');
   // Make sure all stream wrappers are registered.
   // Make sure all stream wrappers are registered.
   file_get_stream_wrappers();
   file_get_stream_wrappers();
   // Ensure mt_rand is reseeded, to prevent random values from one page load
   // Ensure mt_rand is reseeded, to prevent random values from one page load
@@ -5256,8 +5366,8 @@ function drupal_page_set_cache() {
  *
  *
  * Do not call this function from a test. Use $this->cronRun() instead.
  * Do not call this function from a test. Use $this->cronRun() instead.
  *
  *
- * @return
- *   TRUE if cron ran successfully.
+ * @return bool
+ *   TRUE if cron ran successfully and FALSE if cron is already running.
  */
  */
 function drupal_cron_run() {
 function drupal_cron_run() {
   // Allow execution to continue even if the request gets canceled.
   // Allow execution to continue even if the request gets canceled.
@@ -5319,12 +5429,12 @@ function drupal_cron_run() {
       // Do not run if queue wants to skip.
       // Do not run if queue wants to skip.
       continue;
       continue;
     }
     }
-    $function = $info['worker callback'];
+    $callback = $info['worker callback'];
     $end = time() + (isset($info['time']) ? $info['time'] : 15);
     $end = time() + (isset($info['time']) ? $info['time'] : 15);
     $queue = DrupalQueue::get($queue_name);
     $queue = DrupalQueue::get($queue_name);
     while (time() < $end && ($item = $queue->claimItem())) {
     while (time() < $end && ($item = $queue->claimItem())) {
       try {
       try {
-        $function($item->data);
+        call_user_func($callback, $item->data);
         $queue->deleteItem($item);
         $queue->deleteItem($item);
       }
       }
       catch (Exception $e) {
       catch (Exception $e) {
@@ -6277,13 +6387,21 @@ function drupal_render_cid_parts($granularity = NULL) {
   }
   }
 
 
   if (!empty($granularity)) {
   if (!empty($granularity)) {
+    $cache_per_role = $granularity & DRUPAL_CACHE_PER_ROLE;
+    $cache_per_user = $granularity & DRUPAL_CACHE_PER_USER;
+    // User 1 has special permissions outside of the role system, so when
+    // caching per role is requested, it should cache per user instead.
+    if ($user->uid == 1 && $cache_per_role) {
+      $cache_per_user = TRUE;
+      $cache_per_role = FALSE;
+    }
     // 'PER_ROLE' and 'PER_USER' are mutually exclusive. 'PER_USER' can be a
     // 'PER_ROLE' and 'PER_USER' are mutually exclusive. 'PER_USER' can be a
     // resource drag for sites with many users, so when a module is being
     // resource drag for sites with many users, so when a module is being
     // equivocal, we favor the less expensive 'PER_ROLE' pattern.
     // equivocal, we favor the less expensive 'PER_ROLE' pattern.
-    if ($granularity & DRUPAL_CACHE_PER_ROLE) {
+    if ($cache_per_role) {
       $cid_parts[] = 'r.' . implode(',', array_keys($user->roles));
       $cid_parts[] = 'r.' . implode(',', array_keys($user->roles));
     }
     }
-    elseif ($granularity & DRUPAL_CACHE_PER_USER) {
+    elseif ($cache_per_user) {
       $cid_parts[] = "u.$user->uid";
       $cid_parts[] = "u.$user->uid";
     }
     }
 
 
@@ -7023,7 +7141,8 @@ function drupal_uninstall_schema($module) {
  * specification of a schema, as it was defined in a module's
  * specification of a schema, as it was defined in a module's
  * hook_schema(). No additional default values will be set,
  * hook_schema(). No additional default values will be set,
  * hook_schema_alter() is not invoked and these unprocessed
  * hook_schema_alter() is not invoked and these unprocessed
- * definitions won't be cached.
+ * definitions won't be cached. To retrieve the schema after
+ * hook_schema_alter() has been invoked use drupal_get_schema().
  *
  *
  * This function can be used to retrieve a schema specification in
  * This function can be used to retrieve a schema specification in
  * hook_schema(), so it allows you to derive your tables from existing
  * hook_schema(), so it allows you to derive your tables from existing
@@ -7085,6 +7204,24 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
   }
   }
 }
 }
 
 
+/**
+ * Retrieves the type for every field in a table schema.
+ *
+ * @param $table
+ *   The name of the table from which to retrieve type information.
+ *
+ * @return
+ *   An array of types, keyed by field name.
+ */
+function drupal_schema_field_types($table) {
+  $table_schema = drupal_get_schema($table);
+  $field_types = array();
+  foreach ($table_schema['fields'] as $field_name => $field_info) {
+    $field_types[$field_name] = isset($field_info['type']) ? $field_info['type'] : NULL;
+  }
+  return $field_types;
+}
+
 /**
 /**
  * Retrieves a list of fields from a table schema.
  * Retrieves a list of fields from a table schema.
  *
  *
@@ -7286,7 +7423,16 @@ function drupal_write_record($table, &$record, $primary_keys = array()) {
  * Information stored in a module .info file:
  * Information stored in a module .info file:
  * - name: The real name of the module for display purposes.
  * - name: The real name of the module for display purposes.
  * - description: A brief description of the module.
  * - description: A brief description of the module.
- * - dependencies: An array of shortnames of other modules this module requires.
+ * - dependencies: An array of dependency strings. Each is in the form
+ *   'project:module (versions)'; with the following meanings:
+ *   - project: (optional) Project shortname, recommended to ensure uniqueness,
+ *     if the module is part of a project hosted on drupal.org. If omitted,
+ *     also omit the : that follows. The project name is currently ignored by
+ *     Drupal core but is used for automated testing.
+ *   - module: (required) Module shortname within the project.
+ *   - (versions): Optional version information, consisting of one or more
+ *     comma-separated operator/value pairs or simply version numbers, which
+ *     can contain "x" as a wildcard. Examples: (>=7.22, <7.28), (7.x-3.x).
  * - package: The name of the package of modules this module belongs to.
  * - package: The name of the package of modules this module belongs to.
  *
  *
  * See forum.info for an example of a module .info file.
  * See forum.info for an example of a module .info file.
@@ -7366,7 +7512,6 @@ function drupal_parse_info_file($filename) {
  */
  */
 function drupal_parse_info_format($data) {
 function drupal_parse_info_format($data) {
   $info = array();
   $info = array();
-  $constants = get_defined_constants();
 
 
   if (preg_match_all('
   if (preg_match_all('
     @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
     @^\s*                           # Start at the beginning of a line, ignoring leading whitespace
@@ -7406,8 +7551,8 @@ function drupal_parse_info_format($data) {
       }
       }
 
 
       // Handle PHP constants.
       // Handle PHP constants.
-      if (isset($constants[$value])) {
-        $value = $constants[$value];
+      if (preg_match('/^\w+$/i', $value) && defined($value)) {
+        $value = constant($value);
       }
       }
 
 
       // Insert actual value.
       // Insert actual value.
@@ -7571,7 +7716,12 @@ function debug($data, $label = NULL, $print_r = FALSE) {
  * Parses a dependency for comparison by drupal_check_incompatibility().
  * Parses a dependency for comparison by drupal_check_incompatibility().
  *
  *
  * @param $dependency
  * @param $dependency
- *   A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'.
+ *   A dependency string, which specifies a module dependency, and optionally
+ *   the project it comes from and versions that are supported. Supported
+ *   formats include:
+ *   - 'module'
+ *   - 'project:module'
+ *   - 'project:module (>=version, version)'
  *
  *
  * @return
  * @return
  *   An associative array with three keys:
  *   An associative array with three keys:
@@ -7586,6 +7736,12 @@ function debug($data, $label = NULL, $print_r = FALSE) {
  * @see drupal_check_incompatibility()
  * @see drupal_check_incompatibility()
  */
  */
 function drupal_parse_dependency($dependency) {
 function drupal_parse_dependency($dependency) {
+  $value = array();
+  // Split out the optional project name.
+  if (strpos($dependency, ':')) {
+    list($project_name, $dependency) = explode(':', $dependency);
+    $value['project'] = $project_name;
+  }
   // We use named subpatterns and support every op that version_compare
   // We use named subpatterns and support every op that version_compare
   // supports. Also, op is optional and defaults to equals.
   // supports. Also, op is optional and defaults to equals.
   $p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
   $p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
@@ -7594,7 +7750,6 @@ function drupal_parse_dependency($dependency) {
   $p_major = '(?P<major>\d+)';
   $p_major = '(?P<major>\d+)';
   // By setting the minor version to x, branches can be matched.
   // By setting the minor version to x, branches can be matched.
   $p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
   $p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
-  $value = array();
   $parts = explode('(', $dependency, 2);
   $parts = explode('(', $dependency, 2);
   $value['name'] = trim($parts[0]);
   $value['name'] = trim($parts[0]);
   if (isset($parts[1])) {
   if (isset($parts[1])) {
@@ -7709,6 +7864,7 @@ function entity_get_info($entity_type = NULL) {
         // Prepare entity schema fields SQL info for
         // Prepare entity schema fields SQL info for
         // DrupalEntityControllerInterface::buildQuery().
         // DrupalEntityControllerInterface::buildQuery().
         if (isset($entity_info[$name]['base table'])) {
         if (isset($entity_info[$name]['base table'])) {
+          $entity_info[$name]['base table field types'] = drupal_schema_field_types($entity_info[$name]['base table']);
           $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
           $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']);
           if (isset($entity_info[$name]['revision table'])) {
           if (isset($entity_info[$name]['revision table'])) {
             $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);
             $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']);

+ 64 - 8
includes/database/database.inc

@@ -296,6 +296,20 @@ abstract class DatabaseConnection extends PDO {
    */
    */
   protected $prefixReplace = array();
   protected $prefixReplace = array();
 
 
+  /**
+   * List of escaped database, table, and field names, keyed by unescaped names.
+   *
+   * @var array
+   */
+  protected $escapedNames = array();
+
+  /**
+   * List of escaped aliases names, keyed by unescaped aliases.
+   *
+   * @var array
+   */
+  protected $escapedAliases = array();
+
   function __construct($dsn, $username, $password, $driver_options = array()) {
   function __construct($dsn, $username, $password, $driver_options = array()) {
     // Initialize and prepare the connection prefix.
     // Initialize and prepare the connection prefix.
     $this->setPrefix(isset($this->connectionOptions['prefix']) ? $this->connectionOptions['prefix'] : '');
     $this->setPrefix(isset($this->connectionOptions['prefix']) ? $this->connectionOptions['prefix'] : '');
@@ -626,7 +640,7 @@ abstract class DatabaseConnection extends PDO {
    *   A sanitized version of the query comment string.
    *   A sanitized version of the query comment string.
    */
    */
   protected function filterComment($comment = '') {
   protected function filterComment($comment = '') {
-    return preg_replace('/(\/\*\s*)|(\s*\*\/)/', '', $comment);
+    return strtr($comment, array('*' => ' * '));
   }
   }
 
 
   /**
   /**
@@ -656,7 +670,7 @@ abstract class DatabaseConnection extends PDO {
    * @return DatabaseStatementInterface
    * @return DatabaseStatementInterface
    *   This method will return one of: the executed statement, the number of
    *   This method will return one of: the executed statement, the number of
    *   rows affected by the query (not the number matched), or the generated
    *   rows affected by the query (not the number matched), or the generated
-   *   insert IT of the last query, depending on the value of
+   *   insert ID of the last query, depending on the value of
    *   $options['return']. Typically that value will be set by default or a
    *   $options['return']. Typically that value will be set by default or a
    *   query builder and should not be set by a user. If there is an error,
    *   query builder and should not be set by a user. If there is an error,
    *   this method will return NULL and may throw an exception if
    *   this method will return NULL and may throw an exception if
@@ -919,11 +933,14 @@ abstract class DatabaseConnection extends PDO {
    * For some database drivers, it may also wrap the table name in
    * For some database drivers, it may also wrap the table name in
    * database-specific escape characters.
    * database-specific escape characters.
    *
    *
-   * @return
+   * @return string
    *   The sanitized table name string.
    *   The sanitized table name string.
    */
    */
   public function escapeTable($table) {
   public function escapeTable($table) {
-    return preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
+    if (!isset($this->escapedNames[$table])) {
+      $this->escapedNames[$table] = preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
+    }
+    return $this->escapedNames[$table];
   }
   }
 
 
   /**
   /**
@@ -933,11 +950,14 @@ abstract class DatabaseConnection extends PDO {
    * For some database drivers, it may also wrap the field name in
    * For some database drivers, it may also wrap the field name in
    * database-specific escape characters.
    * database-specific escape characters.
    *
    *
-   * @return
+   * @return string
    *   The sanitized field name string.
    *   The sanitized field name string.
    */
    */
   public function escapeField($field) {
   public function escapeField($field) {
-    return preg_replace('/[^A-Za-z0-9_.]+/', '', $field);
+    if (!isset($this->escapedNames[$field])) {
+      $this->escapedNames[$field] = preg_replace('/[^A-Za-z0-9_.]+/', '', $field);
+    }
+    return $this->escapedNames[$field];
   }
   }
 
 
   /**
   /**
@@ -948,11 +968,14 @@ abstract class DatabaseConnection extends PDO {
    * DatabaseConnection::escapeTable(), this doesn't allow the period (".")
    * DatabaseConnection::escapeTable(), this doesn't allow the period (".")
    * because that is not allowed in aliases.
    * because that is not allowed in aliases.
    *
    *
-   * @return
+   * @return string
    *   The sanitized field name string.
    *   The sanitized field name string.
    */
    */
   public function escapeAlias($field) {
   public function escapeAlias($field) {
-    return preg_replace('/[^A-Za-z0-9_]+/', '', $field);
+    if (!isset($this->escapedAliases[$field])) {
+      $this->escapedAliases[$field] = preg_replace('/[^A-Za-z0-9_]+/', '', $field);
+    }
+    return $this->escapedAliases[$field];
   }
   }
 
 
   /**
   /**
@@ -1313,6 +1336,39 @@ abstract class DatabaseConnection extends PDO {
    *   also larger than the $existing_id if one was passed in.
    *   also larger than the $existing_id if one was passed in.
    */
    */
   abstract public function nextId($existing_id = 0);
   abstract public function nextId($existing_id = 0);
+
+  /**
+   * Checks whether utf8mb4 support is configurable in settings.php.
+   *
+   * @return bool
+   */
+  public function utf8mb4IsConfigurable() {
+    // Since 4 byte UTF-8 is not supported by default, there is nothing to
+    // configure.
+    return FALSE;
+  }
+
+  /**
+   * Checks whether utf8mb4 support is currently active.
+   *
+   * @return bool
+   */
+  public function utf8mb4IsActive() {
+    // Since 4 byte UTF-8 is not supported by default, there is nothing to
+    // activate.
+    return FALSE;
+  }
+
+  /**
+   * Checks whether utf8mb4 support is available on the current database system.
+   *
+   * @return bool
+   */
+  public function utf8mb4IsSupported() {
+    // By default we assume that the database backend may not support 4 byte
+    // UTF-8.
+    return FALSE;
+  }
 }
 }
 
 
 /**
 /**

+ 58 - 5
includes/database/mysql/database.inc

@@ -28,6 +28,12 @@ class DatabaseConnection_mysql extends DatabaseConnection {
 
 
     $this->connectionOptions = $connection_options;
     $this->connectionOptions = $connection_options;
 
 
+    $charset = 'utf8';
+    // Check if the charset is overridden to utf8mb4 in settings.php.
+    if ($this->utf8mb4IsActive()) {
+      $charset = 'utf8mb4';
+    }
+
     // The DSN should use either a socket or a host/port.
     // The DSN should use either a socket or a host/port.
     if (isset($connection_options['unix_socket'])) {
     if (isset($connection_options['unix_socket'])) {
       $dsn = 'mysql:unix_socket=' . $connection_options['unix_socket'];
       $dsn = 'mysql:unix_socket=' . $connection_options['unix_socket'];
@@ -36,6 +42,10 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       // Default to TCP connection on port 3306.
       // Default to TCP connection on port 3306.
       $dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . (empty($connection_options['port']) ? 3306 : $connection_options['port']);
       $dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . (empty($connection_options['port']) ? 3306 : $connection_options['port']);
     }
     }
+    // Character set is added to dsn to ensure PDO uses the proper character
+    // set when escaping. This has security implications. See
+    // https://www.drupal.org/node/1201452 for further discussion.
+    $dsn .= ';charset=' . $charset;
     $dsn .= ';dbname=' . $connection_options['database'];
     $dsn .= ';dbname=' . $connection_options['database'];
     // Allow PDO options to be overridden.
     // Allow PDO options to be overridden.
     $connection_options += array(
     $connection_options += array(
@@ -47,6 +57,11 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       // Because MySQL's prepared statements skip the query cache, because it's dumb.
       // Because MySQL's prepared statements skip the query cache, because it's dumb.
       PDO::ATTR_EMULATE_PREPARES => TRUE,
       PDO::ATTR_EMULATE_PREPARES => TRUE,
     );
     );
+    if (defined('PDO::MYSQL_ATTR_MULTI_STATEMENTS')) {
+      // An added connection option in PHP 5.5.21+ to optionally limit SQL to a
+      // single statement like mysqli.
+      $connection_options['pdo'] += array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => FALSE);
+    }
 
 
     parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
     parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
 
 
@@ -54,10 +69,10 @@ class DatabaseConnection_mysql extends DatabaseConnection {
     // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
     // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
     // for UTF-8.
     // for UTF-8.
     if (!empty($connection_options['collation'])) {
     if (!empty($connection_options['collation'])) {
-      $this->exec('SET NAMES utf8 COLLATE ' . $connection_options['collation']);
+      $this->exec('SET NAMES ' . $charset . ' COLLATE ' . $connection_options['collation']);
     }
     }
     else {
     else {
-      $this->exec('SET NAMES utf8');
+      $this->exec('SET NAMES ' . $charset);
     }
     }
 
 
     // Set MySQL init_commands if not already defined.  Default Drupal's MySQL
     // Set MySQL init_commands if not already defined.  Default Drupal's MySQL
@@ -72,10 +87,12 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       'init_commands' => array(),
       'init_commands' => array(),
     );
     );
     $connection_options['init_commands'] += array(
     $connection_options['init_commands'] += array(
-      'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
+      'sql_mode' => "SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
     );
     );
-    // Set connection options.
-    $this->exec(implode('; ', $connection_options['init_commands']));
+    // Execute initial commands.
+    foreach ($connection_options['init_commands'] as $sql) {
+      $this->exec($sql);
+    }
   }
   }
 
 
   public function __destruct() {
   public function __destruct() {
@@ -195,6 +212,42 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       }
       }
     }
     }
   }
   }
+
+  public function utf8mb4IsConfigurable() {
+    return TRUE;
+  }
+
+  public function utf8mb4IsActive() {
+    return isset($this->connectionOptions['charset']) && $this->connectionOptions['charset'] === 'utf8mb4';
+  }
+
+  public function utf8mb4IsSupported() {
+    // Ensure that the MySQL driver supports utf8mb4 encoding.
+    $version = $this->getAttribute(PDO::ATTR_CLIENT_VERSION);
+    if (strpos($version, 'mysqlnd') !== FALSE) {
+      // The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
+      $version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
+      if (version_compare($version, '5.0.9', '<')) {
+        return FALSE;
+      }
+    }
+    else {
+      // The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
+      if (version_compare($version, '5.5.3', '<')) {
+        return FALSE;
+      }
+    }
+
+    // Ensure that the MySQL server supports large prefixes and utf8mb4.
+    try {
+      $this->query("CREATE TABLE {drupal_utf8mb4_test} (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB");
+    }
+    catch (Exception $e) {
+      return FALSE;
+    }
+    $this->query("DROP TABLE {drupal_utf8mb4_test}");
+    return TRUE;
+  }
 }
 }
 
 
 
 

+ 11 - 3
includes/database/mysql/schema.inc

@@ -39,8 +39,8 @@ class DatabaseSchema_mysql extends DatabaseSchema {
       $info['table'] = substr($table, ++$pos);
       $info['table'] = substr($table, ++$pos);
     }
     }
     else {
     else {
-      $db_info = Database::getConnectionInfo();
-      $info['database'] = $db_info[$this->connection->getTarget()]['database'];
+      $db_info = $this->connection->getConnectionOptions();
+      $info['database'] = $db_info['database'];
       $info['table'] = $table;
       $info['table'] = $table;
     }
     }
     return $info;
     return $info;
@@ -81,7 +81,8 @@ class DatabaseSchema_mysql extends DatabaseSchema {
     // Provide defaults if needed.
     // Provide defaults if needed.
     $table += array(
     $table += array(
       'mysql_engine' => 'InnoDB',
       'mysql_engine' => 'InnoDB',
-      'mysql_character_set' => 'utf8',
+      // Allow the default charset to be overridden in settings.php.
+      'mysql_character_set' => $this->connection->utf8mb4IsActive() ? 'utf8mb4' : 'utf8',
     );
     );
 
 
     $sql = "CREATE TABLE {" . $name . "} (\n";
     $sql = "CREATE TABLE {" . $name . "} (\n";
@@ -109,6 +110,13 @@ class DatabaseSchema_mysql extends DatabaseSchema {
       $sql .= ' COLLATE ' . $info['collation'];
       $sql .= ' COLLATE ' . $info['collation'];
     }
     }
 
 
+    // The row format needs to be either DYNAMIC or COMPRESSED in order to allow
+    // for the innodb_large_prefix setting to take effect, see
+    // https://dev.mysql.com/doc/refman/5.6/en/create-table.html
+    if ($this->connection->utf8mb4IsActive()) {
+      $sql .= ' ROW_FORMAT=DYNAMIC';
+    }
+
     // Add table comment.
     // Add table comment.
     if (!empty($table['description'])) {
     if (!empty($table['description'])) {
       $sql .= ' COMMENT ' . $this->prepareComment($table['description'], self::COMMENT_MAX_TABLE);
       $sql .= ' COMMENT ' . $this->prepareComment($table['description'], self::COMMENT_MAX_TABLE);

+ 8 - 0
includes/database/pgsql/database.inc

@@ -216,6 +216,14 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
 
 
     return $id;
     return $id;
   }
   }
+
+  public function utf8mb4IsActive() {
+    return TRUE;
+  }
+
+  public function utf8mb4IsSupported() {
+    return TRUE;
+  }
 }
 }
 
 
 /**
 /**

+ 1 - 1
includes/database/query.inc

@@ -1694,7 +1694,7 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
    * Implements Countable::count().
    * Implements Countable::count().
    *
    *
    * Returns the size of this conditional. The size of the conditional is the
    * Returns the size of this conditional. The size of the conditional is the
-   * size of its conditional array minus one, because one element is the the
+   * size of its conditional array minus one, because one element is the
    * conjunction.
    * conjunction.
    */
    */
   public function count() {
   public function count() {

+ 4 - 1
includes/database/schema.inc

@@ -92,7 +92,8 @@ require_once dirname(__FILE__) . '/query.inc';
  *    specification). Each specification is an array containing the name of
  *    specification). Each specification is an array containing the name of
  *    the referenced table ('table'), and an array of column mappings
  *    the referenced table ('table'), and an array of column mappings
  *    ('columns'). Column mappings are defined by key pairs ('source_column' =>
  *    ('columns'). Column mappings are defined by key pairs ('source_column' =>
- *    'referenced_column').
+ *    'referenced_column'). This key is for documentation purposes only; foreign
+ *    keys are not created in the database, nor are they enforced by Drupal.
  *  - 'indexes':  An associative array of indexes ('indexname' =>
  *  - 'indexes':  An associative array of indexes ('indexname' =>
  *    specification). Each specification is an array of one or more
  *    specification). Each specification is an array of one or more
  *    key column specifiers (see below) that form an index on the
  *    key column specifiers (see below) that form an index on the
@@ -144,6 +145,8 @@ require_once dirname(__FILE__) . '/query.inc';
  *   'unique keys' => array(
  *   'unique keys' => array(
  *     'vid' => array('vid'),
  *     'vid' => array('vid'),
  *   ),
  *   ),
+ *   // For documentation purposes only; foreign keys are not created in the
+ *   // database.
  *   'foreign keys' => array(
  *   'foreign keys' => array(
  *     'node_revision' => array(
  *     'node_revision' => array(
  *       'table' => 'node_revision',
  *       'table' => 'node_revision',

+ 15 - 0
includes/database/select.inc

@@ -1231,6 +1231,21 @@ class SelectQuery extends Query implements SelectQueryInterface {
 
 
     // Modules may alter all queries or only those having a particular tag.
     // Modules may alter all queries or only those having a particular tag.
     if (isset($this->alterTags)) {
     if (isset($this->alterTags)) {
+      // Many contrib modules assume that query tags used for access-checking
+      // purposes follow the pattern $entity_type . '_access'. But this is
+      // not the case for taxonomy terms, since core used to add term_access
+      // instead of taxonomy_term_access to its queries. Provide backwards
+      // compatibility by adding both tags here instead of attempting to fix
+      // all contrib modules in a coordinated effort.
+      // TODO:
+      // - Extract this mechanism into a hook as part of a public (non-security)
+      //   issue.
+      // - Emit E_USER_DEPRECATED if term_access is used.
+      //   https://www.drupal.org/node/2575081
+      $term_access_tags = array('term_access' => 1, 'taxonomy_term_access' => 1);
+      if (array_intersect_key($this->alterTags, $term_access_tags)) {
+        $this->alterTags += $term_access_tags;
+      }
       $hooks = array('query');
       $hooks = array('query');
       foreach ($this->alterTags as $tag => $value) {
       foreach ($this->alterTags as $tag => $value) {
         $hooks[] = 'query_' . $tag;
         $hooks[] = 'query_' . $tag;

+ 8 - 0
includes/database/sqlite/database.inc

@@ -378,6 +378,14 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
     }
     }
   }
   }
 
 
+  public function utf8mb4IsActive() {
+    return TRUE;
+  }
+
+  public function utf8mb4IsSupported() {
+    return TRUE;
+  }
+
 }
 }
 
 
 /**
 /**

+ 0 - 2
includes/database/sqlite/install.inc

@@ -14,8 +14,6 @@ class DatabaseTasks_sqlite extends DatabaseTasks {
 
 
   /**
   /**
    * Minimum engine version.
    * Minimum engine version.
-   *
-   * @todo: consider upping to 3.6.8 in Drupal 8 to get SAVEPOINT support.
    */
    */
   public function minimumVersion() {
   public function minimumVersion() {
     return '3.3.7';
     return '3.3.7';

+ 74 - 12
includes/entity.inc

@@ -28,7 +28,9 @@ interface DrupalEntityControllerInterface {
    * @param $ids
    * @param $ids
    *   An array of entity IDs, or FALSE to load all entities.
    *   An array of entity IDs, or FALSE to load all entities.
    * @param $conditions
    * @param $conditions
-   *   An array of conditions in the form 'field' => $value.
+   *   An array of conditions. Keys are field names on the entity's base table.
+   *   Values will be compared for equality. All the comparisons will be ANDed
+   *   together. This parameter is deprecated; use an EntityFieldQuery instead.
    *
    *
    * @return
    * @return
    *   An array of entity objects indexed by their ids. When no results are
    *   An array of entity objects indexed by their ids. When no results are
@@ -181,6 +183,11 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
       }
       }
     }
     }
 
 
+    // Ensure integer entity IDs are valid.
+    if (!empty($ids)) {
+      $this->cleanIds($ids);
+    }
+
     // Load any remaining entities from the database. This is the case if $ids
     // Load any remaining entities from the database. This is the case if $ids
     // is set to FALSE (so we load all entities), if there are any ids left to
     // is set to FALSE (so we load all entities), if there are any ids left to
     // load, if loading a revision, or if $conditions was passed without $ids.
     // load, if loading a revision, or if $conditions was passed without $ids.
@@ -221,6 +228,35 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
     return $entities;
     return $entities;
   }
   }
 
 
+  /**
+   * Ensures integer entity IDs are valid.
+   *
+   * The identifier sanitization provided by this method has been introduced
+   * as Drupal used to rely on the database to facilitate this, which worked
+   * correctly with MySQL but led to errors with other DBMS such as PostgreSQL.
+   *
+   * @param array $ids
+   *   The entity IDs to verify. Non-integer IDs are removed from this array if
+   *   the entity type requires IDs to be integers.
+   */
+  protected function cleanIds(&$ids) {
+    $entity_info = entity_get_info($this->entityType);
+    if (isset($entity_info['base table field types'])) {
+      $id_type = $entity_info['base table field types'][$this->idKey];
+      if ($id_type == 'serial' || $id_type == 'int') {
+        $ids = array_filter($ids, array($this, 'filterId'));
+        $ids = array_map('intval', $ids);
+      }
+    }
+  }
+
+  /**
+   * Callback for array_filter that removes non-integer IDs.
+   */
+  protected function filterId($id) {
+    return is_numeric($id) && $id == (int) $id;
+  }
+
   /**
   /**
    * Builds the query to load the entity.
    * Builds the query to load the entity.
    *
    *
@@ -236,7 +272,9 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
    * @param $ids
    * @param $ids
    *   An array of entity IDs, or FALSE to load all entities.
    *   An array of entity IDs, or FALSE to load all entities.
    * @param $conditions
    * @param $conditions
-   *   An array of conditions in the form 'field' => $value.
+   *   An array of conditions. Keys are field names on the entity's base table.
+   *   Values will be compared for equality. All the comparisons will be ANDed
+   *   together. This parameter is deprecated; use an EntityFieldQuery instead.
    * @param $revision_id
    * @param $revision_id
    *   The ID of the revision to load, or FALSE if this query is asking for the
    *   The ID of the revision to load, or FALSE if this query is asking for the
    *   most current revision(s).
    *   most current revision(s).
@@ -408,7 +446,7 @@ class EntityFieldQueryException extends Exception {}
  *
  *
  * This class allows finding entities based on entity properties (for example,
  * This class allows finding entities based on entity properties (for example,
  * node->changed), field values, and generic entity meta data (bundle,
  * node->changed), field values, and generic entity meta data (bundle,
- * entity type, entity id, and revision ID). It is not possible to query across
+ * entity type, entity ID, and revision ID). It is not possible to query across
  * multiple entity types. For example, there is no facility to find published
  * multiple entity types. For example, there is no facility to find published
  * nodes written by users created in the last hour, as this would require
  * nodes written by users created in the last hour, as this would require
  * querying both node->status and user->created.
  * querying both node->status and user->created.
@@ -650,14 +688,36 @@ class EntityFieldQuery {
    * @param $field
    * @param $field
    *   Either a field name or a field array.
    *   Either a field name or a field array.
    * @param $column
    * @param $column
-   *   The column that should hold the value to be matched.
+   *   The column that should hold the value to be matched, defined in the
+   *   hook_field_schema() of this field. If this is omitted then all of the
+   *   other parameters are ignored, except $field, and this call will just be
+   *   adding a condition that says that the field has a value, rather than
+   *   testing the value itself.
    * @param $value
    * @param $value
-   *   The value to test the column value against.
+   *   The value to test the column value against. In most cases, this is a
+   *   scalar. For more complex options, it is an array. The meaning of each
+   *   element in the array is dependent on $operator.
    * @param $operator
    * @param $operator
-   *   The operator to be used to test the given value.
+   *   The operator to be used to test the given value. The possible values are:
+   *   - '=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS': These
+   *     operators expect $value to be a literal of the same type as the
+   *     column.
+   *   - 'IN', 'NOT IN': These operators expect $value to be an array of
+   *     literals of the same type as the column.
+   *   - 'BETWEEN': This operator expects $value to be an array of two literals
+   *     of the same type as the column.
+   *   The operator can be omitted, and will default to 'IN' if the value is an
+   *   array, or to '=' otherwise.
    * @param $delta_group
    * @param $delta_group
    *   An arbitrary identifier: conditions in the same group must have the same
    *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
+   *   $delta_group. For example, let's presume a multivalue field which has
+   *   two columns, 'color' and 'shape', and for entity ID 1, there are two
+   *   values: red/square and blue/circle. Entity ID 1 does not have values
+   *   corresponding to 'red circle'; however if you pass 'red' and 'circle' as
+   *   conditions, it will appear in the results -- by default queries will run
+   *   against any combination of deltas. By passing the conditions with the
+   *   same $delta_group it will ensure that only values attached to the same
+   *   delta are matched, and entity 1 would then be excluded from the results.
    * @param $language_group
    * @param $language_group
    *   An arbitrary identifier: conditions in the same group must have the same
    *   An arbitrary identifier: conditions in the same group must have the same
    *   $language_group.
    *   $language_group.
@@ -732,9 +792,11 @@ class EntityFieldQuery {
    * @param $field
    * @param $field
    *   Either a field name or a field array.
    *   Either a field name or a field array.
    * @param $column
    * @param $column
-   *   A column defined in the hook_field_schema() of this field. If this is
-   *   omitted then the query will find only entities that have data in this
-   *   field, using the entity and property conditions if there are any.
+   *   The column that should hold the value to be matched, defined in the
+   *   hook_field_schema() of this field. If this is omitted then all of the
+   *   other parameters are ignored, except $field, and this call will just be
+   *   adding a condition that says that the field has a value, rather than
+   *   testing the value itself.
    * @param $value
    * @param $value
    *   The value to test the column value against. In most cases, this is a
    *   The value to test the column value against. In most cases, this is a
    *   scalar. For more complex options, it is an array. The meaning of each
    *   scalar. For more complex options, it is an array. The meaning of each
@@ -753,10 +815,10 @@ class EntityFieldQuery {
    * @param $delta_group
    * @param $delta_group
    *   An arbitrary identifier: conditions in the same group must have the same
    *   An arbitrary identifier: conditions in the same group must have the same
    *   $delta_group. For example, let's presume a multivalue field which has
    *   $delta_group. For example, let's presume a multivalue field which has
-   *   two columns, 'color' and 'shape', and for entity id 1, there are two
+   *   two columns, 'color' and 'shape', and for entity ID 1, there are two
    *   values: red/square and blue/circle. Entity ID 1 does not have values
    *   values: red/square and blue/circle. Entity ID 1 does not have values
    *   corresponding to 'red circle', however if you pass 'red' and 'circle' as
    *   corresponding to 'red circle', however if you pass 'red' and 'circle' as
-   *   conditions, it will appear in the  results - by default queries will run
+   *   conditions, it will appear in the results -- by default queries will run
    *   against any combination of deltas. By passing the conditions with the
    *   against any combination of deltas. By passing the conditions with the
    *   same $delta_group it will ensure that only values attached to the same
    *   same $delta_group it will ensure that only values attached to the same
    *   delta are matched, and entity 1 would then be excluded from the results.
    *   delta are matched, and entity 1 would then be excluded from the results.

+ 10 - 1
includes/errors.inc

@@ -199,7 +199,16 @@ function _drupal_log_error($error, $fatal = FALSE) {
     $number++;
     $number++;
   }
   }
 
 
-  watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
+  // Log the error immediately, unless this is a non-fatal error which has been
+  // triggered via drupal_trigger_error_with_delayed_logging(); in that case
+  // trigger it in a shutdown function. Fatal errors are always triggered
+  // immediately since for a fatal error the page request will end here anyway.
+  if (!$fatal && drupal_static('_drupal_trigger_error_with_delayed_logging')) {
+    drupal_register_shutdown_function('watchdog', 'php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
+  }
+  else {
+    watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
+  }
 
 
   if ($fatal) {
   if ($fatal) {
     drupal_add_http_header('Status', '500 Service unavailable (with message)');
     drupal_add_http_header('Status', '500 Service unavailable (with message)');

+ 6 - 4
includes/file.inc

@@ -273,7 +273,9 @@ function file_default_scheme() {
  *   The normalized URI.
  *   The normalized URI.
  */
  */
 function file_stream_wrapper_uri_normalize($uri) {
 function file_stream_wrapper_uri_normalize($uri) {
-  $scheme = file_uri_scheme($uri);
+  // Inline file_uri_scheme() function call for performance reasons.
+  $position = strpos($uri, '://');
+  $scheme = $position ? substr($uri, 0, $position) : FALSE;
 
 
   if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
   if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
     $target = file_uri_target($uri);
     $target = file_uri_target($uri);
@@ -1559,7 +1561,7 @@ function file_save_upload($form_field_name, $validators = array(), $destination
     return FALSE;
     return FALSE;
   }
   }
 
 
-  // Add in our check of the the file name length.
+  // Add in our check of the file name length.
   $validators['file_validate_name_length'] = array();
   $validators['file_validate_name_length'] = array();
 
 
   // Call the validation functions specified by this function's caller.
   // Call the validation functions specified by this function's caller.
@@ -1785,7 +1787,7 @@ function file_validate_is_image(stdClass $file) {
 /**
 /**
  * Verifies that image dimensions are within the specified maximum and minimum.
  * Verifies that image dimensions are within the specified maximum and minimum.
  *
  *
- * Non-image files will be ignored. If a image toolkit is available the image
+ * Non-image files will be ignored. If an image toolkit is available the image
  * will be scaled to fit within the desired maximum dimensions.
  * will be scaled to fit within the desired maximum dimensions.
  *
  *
  * @param $file
  * @param $file
@@ -2022,7 +2024,7 @@ function file_download() {
  *
  *
  * @see file_transfer()
  * @see file_transfer()
  * @see file_download_access()
  * @see file_download_access()
- * @see hook_file_downlaod()
+ * @see hook_file_download()
  */
  */
 function file_download_headers($uri) {
 function file_download_headers($uri) {
   // Let other modules provide headers and control access to the file.
   // Let other modules provide headers and control access to the file.

+ 146 - 26
includes/form.inc

@@ -105,7 +105,8 @@
  *   generate the same form (or very similar forms) using different $form_ids
  *   generate the same form (or very similar forms) using different $form_ids
  *   can implement hook_forms(), which maps different $form_id values to the
  *   can implement hook_forms(), which maps different $form_id values to the
  *   proper form constructor function. Examples may be found in node_forms(),
  *   proper form constructor function. Examples may be found in node_forms(),
- *   and search_forms().
+ *   and search_forms(). hook_forms() can also be used to define forms in
+ *   classes.
  * @param ...
  * @param ...
  *   Any additional arguments are passed on to the functions called by
  *   Any additional arguments are passed on to the functions called by
  *   drupal_get_form(), including the unique form constructor function. For
  *   drupal_get_form(), including the unique form constructor function. For
@@ -809,7 +810,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
     }
     }
     if (isset($form_definition['callback'])) {
     if (isset($form_definition['callback'])) {
       $callback = $form_definition['callback'];
       $callback = $form_definition['callback'];
-      $form_state['build_info']['base_form_id'] = $callback;
+      $form_state['build_info']['base_form_id'] = isset($form_definition['base_form_id']) ? $form_definition['base_form_id'] : $callback;
     }
     }
     // In case $form_state['wrapper_callback'] is not defined already, we also
     // In case $form_state['wrapper_callback'] is not defined already, we also
     // allow hook_forms() to define one.
     // allow hook_forms() to define one.
@@ -830,7 +831,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
   // the actual form builder function ($callback) expects. This allows for
   // the actual form builder function ($callback) expects. This allows for
   // pre-populating a form with common elements for certain forms, such as
   // pre-populating a form with common elements for certain forms, such as
   // back/next/save buttons in multi-step form wizards. See drupal_build_form().
   // back/next/save buttons in multi-step form wizards. See drupal_build_form().
-  if (isset($form_state['wrapper_callback']) && function_exists($form_state['wrapper_callback'])) {
+  if (isset($form_state['wrapper_callback']) && is_callable($form_state['wrapper_callback'])) {
     $form = call_user_func_array($form_state['wrapper_callback'], $args);
     $form = call_user_func_array($form_state['wrapper_callback'], $args);
     // Put the prepopulated $form into $args.
     // Put the prepopulated $form into $args.
     $args[0] = $form;
     $args[0] = $form;
@@ -938,7 +939,7 @@ function drupal_process_form($form_id, &$form, &$form_state) {
         // after the batch is processed.
         // after the batch is processed.
       }
       }
 
 
-      // Set a flag to indicate the the form has been processed and executed.
+      // Set a flag to indicate that the form has been processed and executed.
       $form_state['executed'] = TRUE;
       $form_state['executed'] = TRUE;
 
 
       // Redirect the form based on values in $form_state.
       // Redirect the form based on values in $form_state.
@@ -1128,6 +1129,17 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
   drupal_alter($hooks, $form, $form_state, $form_id);
   drupal_alter($hooks, $form, $form_state, $form_id);
 }
 }
 
 
+/**
+ * Helper function to call form_set_error() if there is a token error.
+ */
+function _drupal_invalid_token_set_form_error() {
+  $path = current_path();
+  $query = drupal_get_query_parameters();
+  $url = url($path, array('query' => $query));
+
+  // Setting this error will cause the form to fail validation.
+  form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url)));
+}
 
 
 /**
 /**
  * Validates user-submitted form data in the $form_state array.
  * Validates user-submitted form data in the $form_state array.
@@ -1162,16 +1174,11 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
   }
   }
 
 
   // If the session token was set by drupal_prepare_form(), ensure that it
   // If the session token was set by drupal_prepare_form(), ensure that it
-  // matches the current user's session.
+  // matches the current user's session. This is duplicate to code in
+  // form_builder() but left to protect any custom form handling code.
   if (isset($form['#token'])) {
   if (isset($form['#token'])) {
-    if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
-      $path = current_path();
-      $query = drupal_get_query_parameters();
-      $url = url($path, array('query' => $query));
-
-      // Setting this error will cause the form to fail validation.
-      form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url)));
-
+    if (!drupal_valid_token($form_state['values']['form_token'], $form['#token']) || !empty($form_state['invalid_token'])) {
+      _drupal_invalid_token_set_form_error();
       // Stop here and don't run any further validation handlers, because they
       // Stop here and don't run any further validation handlers, because they
       // could invoke non-safe operations which opens the door for CSRF
       // could invoke non-safe operations which opens the door for CSRF
       // vulnerabilities.
       // vulnerabilities.
@@ -1827,6 +1834,20 @@ function form_builder($form_id, &$element, &$form_state) {
     // from the POST data is set and matches the current form_id.
     // from the POST data is set and matches the current form_id.
     if ($form_state['programmed'] || (!empty($form_state['input']) && (isset($form_state['input']['form_id']) && ($form_state['input']['form_id'] == $form_id)))) {
     if ($form_state['programmed'] || (!empty($form_state['input']) && (isset($form_state['input']['form_id']) && ($form_state['input']['form_id'] == $form_id)))) {
       $form_state['process_input'] = TRUE;
       $form_state['process_input'] = TRUE;
+      // If the session token was set by drupal_prepare_form(), ensure that it
+      // matches the current user's session.
+      $form_state['invalid_token'] = FALSE;
+      if (isset($element['#token'])) {
+        if (empty($form_state['input']['form_token']) || !drupal_valid_token($form_state['input']['form_token'], $element['#token'])) {
+          // Set an early form error to block certain input processing since that
+          // opens the door for CSRF vulnerabilities.
+          _drupal_invalid_token_set_form_error();
+          // This value is checked in _form_builder_handle_input_element().
+          $form_state['invalid_token'] = TRUE;
+          // Make sure file uploads do not get processed.
+          $_FILES = array();
+        }
+      }
     }
     }
     else {
     else {
       $form_state['process_input'] = FALSE;
       $form_state['process_input'] = FALSE;
@@ -1930,6 +1951,18 @@ function form_builder($form_id, &$element, &$form_state) {
       $element['#attributes']['enctype'] = 'multipart/form-data';
       $element['#attributes']['enctype'] = 'multipart/form-data';
     }
     }
 
 
+    // Allow Ajax submissions to the form action to bypass verification. This is
+    // especially useful for multipart forms, which cannot be verified via a
+    // response header.
+    $element['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array(
+        'urlIsAjaxTrusted' => array(
+          $element['#action'] => TRUE,
+        ),
+      ),
+    );
+
     // If a form contains a single textfield, and the ENTER key is pressed
     // If a form contains a single textfield, and the ENTER key is pressed
     // within it, Internet Explorer submits the form with no POST data
     // within it, Internet Explorer submits the form with no POST data
     // identifying any submit button. Other browsers submit POST data as though
     // identifying any submit button. Other browsers submit POST data as though
@@ -1978,6 +2011,19 @@ function form_builder($form_id, &$element, &$form_state) {
  * Adds the #name and #value properties of an input element before rendering.
  * Adds the #name and #value properties of an input element before rendering.
  */
  */
 function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
 function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
+  static $safe_core_value_callbacks = array(
+    'form_type_token_value',
+    'form_type_textarea_value',
+    'form_type_textfield_value',
+    'form_type_checkbox_value',
+    'form_type_checkboxes_value',
+    'form_type_radios_value',
+    'form_type_password_confirm_value',
+    'form_type_select_value',
+    'form_type_tableselect_value',
+    'list_boolean_allowed_values_callback',
+  );
+
   if (!isset($element['#name'])) {
   if (!isset($element['#name'])) {
     $name = array_shift($element['#parents']);
     $name = array_shift($element['#parents']);
     $element['#name'] = $name;
     $element['#name'] = $name;
@@ -2056,7 +2102,14 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
       // property, optionally filtered through $value_callback.
       // property, optionally filtered through $value_callback.
       if ($input_exists) {
       if ($input_exists) {
         if (function_exists($value_callback)) {
         if (function_exists($value_callback)) {
-          $element['#value'] = $value_callback($element, $input, $form_state);
+          // Skip all value callbacks except safe ones like text if the CSRF
+          // token was invalid.
+          if (empty($form_state['invalid_token']) || in_array($value_callback, $safe_core_value_callbacks)) {
+            $element['#value'] = $value_callback($element, $input, $form_state);
+          }
+          else {
+            $input = NULL;
+          }
         }
         }
         if (!isset($element['#value']) && isset($input)) {
         if (!isset($element['#value']) && isset($input)) {
           $element['#value'] = $input;
           $element['#value'] = $input;
@@ -2451,6 +2504,17 @@ function form_type_password_confirm_value($element, $input = FALSE) {
     $element += array('#default_value' => array());
     $element += array('#default_value' => array());
     return $element['#default_value'] + array('pass1' => '', 'pass2' => '');
     return $element['#default_value'] + array('pass1' => '', 'pass2' => '');
   }
   }
+  $value = array('pass1' => '', 'pass2' => '');
+  // Throw out all invalid array keys; we only allow pass1 and pass2.
+  foreach ($value as $allowed_key => $default) {
+    // These should be strings, but allow other scalars since they might be
+    // valid input in programmatic form submissions. Any nested array values
+    // are ignored.
+    if (isset($input[$allowed_key]) && is_scalar($input[$allowed_key])) {
+      $value[$allowed_key] = (string) $input[$allowed_key];
+    }
+  }
+  return $value;
 }
 }
 
 
 /**
 /**
@@ -2494,6 +2558,27 @@ function form_type_select_value($element, $input = FALSE) {
   }
   }
 }
 }
 
 
+/**
+ * Determines the value for a textarea form element.
+ *
+ * @param array $element
+ *   The form element whose value is being populated.
+ * @param mixed $input
+ *   The incoming input to populate the form element. If this is FALSE,
+ *   the element's default value should be returned.
+ *
+ * @return string
+ *   The data that will appear in the $element_state['values'] collection
+ *   for this element. Return nothing to use the default.
+ */
+function form_type_textarea_value($element, $input = FALSE) {
+  if ($input !== FALSE && $input !== NULL) {
+    // This should be a string, but allow other scalars since they might be
+    // valid input in programmatic form submissions.
+    return is_scalar($input) ? (string) $input : '';
+  }
+}
+
 /**
 /**
  * Determines the value for a textfield form element.
  * Determines the value for a textfield form element.
  *
  *
@@ -2509,9 +2594,12 @@ function form_type_select_value($element, $input = FALSE) {
  */
  */
 function form_type_textfield_value($element, $input = FALSE) {
 function form_type_textfield_value($element, $input = FALSE) {
   if ($input !== FALSE && $input !== NULL) {
   if ($input !== FALSE && $input !== NULL) {
-    // Equate $input to the form value to ensure it's marked for
-    // validation.
-    return str_replace(array("\r", "\n"), '', $input);
+    // This should be a string, but allow other scalars since they might be
+    // valid input in programmatic form submissions.
+    if (!is_scalar($input)) {
+      $input = '';
+    }
+    return str_replace(array("\r", "\n"), '', (string) $input);
   }
   }
 }
 }
 
 
@@ -2627,8 +2715,8 @@ function _form_options_flatten($array) {
  *   - #required: (optional) Whether the user needs to select an option (TRUE)
  *   - #required: (optional) Whether the user needs to select an option (TRUE)
  *     or not (FALSE). Defaults to FALSE.
  *     or not (FALSE). Defaults to FALSE.
  *   - #empty_option: (optional) The label to show for the first default option.
  *   - #empty_option: (optional) The label to show for the first default option.
- *     By default, the label is automatically set to "- Please select -" for a
- *     required field and "- None -" for an optional field.
+ *     By default, the label is automatically set to "- Select -" for a required
+ *     field and "- None -" for an optional field.
  *   - #empty_value: (optional) The value for the first default option, which is
  *   - #empty_value: (optional) The value for the first default option, which is
  *     used to determine whether the user submitted a value or not.
  *     used to determine whether the user submitted a value or not.
  *     - If #required is TRUE, this defaults to '' (an empty string).
  *     - If #required is TRUE, this defaults to '' (an empty string).
@@ -2941,7 +3029,7 @@ function form_process_password_confirm($element) {
 function password_confirm_validate($element, &$element_state) {
 function password_confirm_validate($element, &$element_state) {
   $pass1 = trim($element['pass1']['#value']);
   $pass1 = trim($element['pass1']['#value']);
   $pass2 = trim($element['pass2']['#value']);
   $pass2 = trim($element['pass2']['#value']);
-  if (!empty($pass1) || !empty($pass2)) {
+  if (strlen($pass1) > 0 || strlen($pass2) > 0) {
     if (strcmp($pass1, $pass2)) {
     if (strcmp($pass1, $pass2)) {
       form_error($element, t('The specified passwords do not match.'));
       form_error($element, t('The specified passwords do not match.'));
     }
     }
@@ -3298,9 +3386,12 @@ function form_process_container($element, &$form_state) {
 /**
 /**
  * Returns HTML to wrap child elements in a container.
  * Returns HTML to wrap child elements in a container.
  *
  *
- * Used for grouped form items. Can also be used as a #theme_wrapper for any
+ * Used for grouped form items. Can also be used as a theme wrapper for any
  * renderable element, to surround it with a <div> and add attributes such as
  * renderable element, to surround it with a <div> and add attributes such as
- * classes or an HTML id.
+ * classes or an HTML ID.
+ *
+ * See the @link forms_api_reference.html Form API reference @endlink for more
+ * information on the #theme_wrappers render array property.
  *
  *
  * @param $variables
  * @param $variables
  *   An associative array containing:
  *   An associative array containing:
@@ -3455,6 +3546,7 @@ function form_process_tableselect($element) {
             '#return_value' => $key,
             '#return_value' => $key,
             '#default_value' => isset($value[$key]) ? $key : NULL,
             '#default_value' => isset($value[$key]) ? $key : NULL,
             '#attributes' => $element['#attributes'],
             '#attributes' => $element['#attributes'],
+            '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
           );
           );
         }
         }
         else {
         else {
@@ -3875,6 +3967,34 @@ function theme_hidden($variables) {
   return '<input' . drupal_attributes($element['#attributes']) . " />\n";
   return '<input' . drupal_attributes($element['#attributes']) . " />\n";
 }
 }
 
 
+/**
+ * Process function to prepare autocomplete data.
+ *
+ * @param $element
+ *   A textfield or other element with a #autocomplete_path.
+ *
+ * @return array
+ *   The processed form element.
+ */
+function form_process_autocomplete($element) {
+  $element['#autocomplete_input'] = array();
+  if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
+    $element['#autocomplete_input']['#id'] = $element['#id'] .'-autocomplete';
+    // Force autocomplete to use non-clean URLs since this protects against the
+    // browser interpreting the path plus search string as an actual file.
+    $current_clean_url = isset($GLOBALS['conf']['clean_url']) ? $GLOBALS['conf']['clean_url'] : NULL;
+    $GLOBALS['conf']['clean_url'] = 0;
+    // Force the script path to 'index.php', in case the server is not
+    // configured to find it automatically. Normally it is the responsibility
+    // of the site to do this themselves using hook_url_outbound_alter() (see
+    // url()) but since this code is forcing non-clean URLs on sites that don't
+    // normally use them, it is done here instead.
+    $element['#autocomplete_input']['#url_value'] = url($element['#autocomplete_path'], array('absolute' => TRUE, 'script' => 'index.php'));
+    $GLOBALS['conf']['clean_url'] = $current_clean_url;
+  }
+  return $element;
+}
+
 /**
 /**
  * Returns HTML for a textfield form element.
  * Returns HTML for a textfield form element.
  *
  *
@@ -3893,14 +4013,14 @@ function theme_textfield($variables) {
   _form_set_class($element, array('form-text'));
   _form_set_class($element, array('form-text'));
 
 
   $extra = '';
   $extra = '';
-  if ($element['#autocomplete_path'] && drupal_valid_path($element['#autocomplete_path'])) {
+  if ($element['#autocomplete_path'] && !empty($element['#autocomplete_input'])) {
     drupal_add_library('system', 'drupal.autocomplete');
     drupal_add_library('system', 'drupal.autocomplete');
     $element['#attributes']['class'][] = 'form-autocomplete';
     $element['#attributes']['class'][] = 'form-autocomplete';
 
 
     $attributes = array();
     $attributes = array();
     $attributes['type'] = 'hidden';
     $attributes['type'] = 'hidden';
-    $attributes['id'] = $element['#attributes']['id'] . '-autocomplete';
-    $attributes['value'] = url($element['#autocomplete_path'], array('absolute' => TRUE));
+    $attributes['id'] = $element['#autocomplete_input']['#id'];
+    $attributes['value'] = $element['#autocomplete_input']['#url_value'];
     $attributes['disabled'] = 'disabled';
     $attributes['disabled'] = 'disabled';
     $attributes['class'][] = 'autocomplete';
     $attributes['class'][] = 'autocomplete';
     $extra = '<input' . drupal_attributes($attributes) . ' />';
     $extra = '<input' . drupal_attributes($attributes) . ' />';
@@ -4374,7 +4494,7 @@ function element_validate_number($element, &$form_state) {
  *
  *
  * Sample callback_batch_finished():
  * Sample callback_batch_finished():
  * @code
  * @code
- * function batch_test_finished($success, $results, $operations) {
+ * function my_finished_callback($success, $results, $operations) {
  *   // The 'success' parameter means no fatal PHP errors were detected. All
  *   // The 'success' parameter means no fatal PHP errors were detected. All
  *   // other error management should be handled using 'results'.
  *   // other error management should be handled using 'results'.
  *   if ($success) {
  *   if ($success) {

+ 19 - 3
includes/install.core.inc

@@ -362,7 +362,8 @@ function install_run_tasks(&$install_state) {
  * Runs an individual installation task.
  * Runs an individual installation task.
  *
  *
  * @param $task
  * @param $task
- *   An array of information about the task to be run.
+ *   An array of information about the task to be run as returned by
+ *   hook_install_tasks().
  * @param $install_state
  * @param $install_state
  *   An array of information about the current installation state. This is
  *   An array of information about the current installation state. This is
  *   passed in by reference so that it can be modified by the task.
  *   passed in by reference so that it can be modified by the task.
@@ -478,11 +479,15 @@ function install_run_task($task, &$install_state) {
  * the page request evolves (for example, if an installation profile hasn't
  * the page request evolves (for example, if an installation profile hasn't
  * been selected yet, we don't yet know which profile tasks need to be run).
  * been selected yet, we don't yet know which profile tasks need to be run).
  *
  *
+ * You can override this using hook_install_tasks() or
+ * hook_install_tasks_alter().
+ *
  * @param $install_state
  * @param $install_state
  *   An array of information about the current installation state.
  *   An array of information about the current installation state.
  *
  *
  * @return
  * @return
- *   A list of tasks to be performed, with associated metadata.
+ *   A list of tasks to be performed, with associated metadata as returned by
+ *   hook_install_tasks().
  */
  */
 function install_tasks_to_perform($install_state) {
 function install_tasks_to_perform($install_state) {
   // Start with a list of all currently available tasks.
   // Start with a list of all currently available tasks.
@@ -804,6 +809,13 @@ function install_system_module(&$install_state) {
 
 
   variable_set('install_profile_modules', array_diff($modules, array('system')));
   variable_set('install_profile_modules', array_diff($modules, array('system')));
   $install_state['database_tables_exist'] = TRUE;
   $install_state['database_tables_exist'] = TRUE;
+
+  // Prevent the hook_requirements() check from telling us to convert the
+  // database to utf8mb4.
+  $connection = Database::getConnection();
+  if ($connection->utf8mb4IsConfigurable() && $connection->utf8mb4IsActive()) {
+    variable_set('drupal_all_databases_are_utf8mb4', TRUE);
+  }
 }
 }
 
 
 /**
 /**
@@ -1585,7 +1597,9 @@ function install_finished(&$install_state) {
 }
 }
 
 
 /**
 /**
- * Batch callback for batch installation of modules.
+ * Implements callback_batch_operation().
+ *
+ * Performs batch installation of modules.
  */
  */
 function _install_module_batch($module, $module_name, &$context) {
 function _install_module_batch($module, $module_name, &$context) {
   // Install and enable the module right away, so that the module will be
   // Install and enable the module right away, so that the module will be
@@ -1598,6 +1612,8 @@ function _install_module_batch($module, $module_name, &$context) {
 }
 }
 
 
 /**
 /**
+ * Implements callback_batch_finished().
+ *
  * 'Finished' callback for module installation batch.
  * 'Finished' callback for module installation batch.
  */
  */
 function _install_profile_modules_finished($success, $results, $operations) {
 function _install_profile_modules_finished($success, $results, $operations) {

+ 9 - 1
includes/install.inc

@@ -653,6 +653,13 @@ function drupal_rewrite_settings($settings = array(), $prefix = '') {
     if ($fp && fwrite($fp, $buffer) === FALSE) {
     if ($fp && fwrite($fp, $buffer) === FALSE) {
       throw new Exception(st('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
       throw new Exception(st('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
     }
     }
+    else {
+      // The existing settings.php file might have been included already. In
+      // case an opcode cache is enabled, the rewritten contents of the file
+      // will not be reflected in this process. Ensure to invalidate the file
+      // in case an opcode cache is enabled.
+      drupal_clear_opcode_cache(DRUPAL_ROOT . '/' . $settings_file);
+    }
   }
   }
   else {
   else {
     throw new Exception(st('Failed to open %settings. Verify the file permissions.', array('%settings' => $default_settings)));
     throw new Exception(st('Failed to open %settings. Verify the file permissions.', array('%settings' => $default_settings)));
@@ -743,7 +750,7 @@ function drupal_install_system() {
 /**
 /**
  * Uninstalls a given list of disabled modules.
  * Uninstalls a given list of disabled modules.
  *
  *
- * @param array $module_list
+ * @param string[] $module_list
  *   The modules to uninstall. It is the caller's responsibility to ensure that
  *   The modules to uninstall. It is the caller's responsibility to ensure that
  *   all modules in this list have already been disabled before this function
  *   all modules in this list have already been disabled before this function
  *   is called.
  *   is called.
@@ -762,6 +769,7 @@ function drupal_install_system() {
  *   included in $module_list).
  *   included in $module_list).
  *
  *
  * @see module_disable()
  * @see module_disable()
+ * @see module_enable()
  */
  */
 function drupal_uninstall_modules($module_list = array(), $uninstall_dependents = TRUE) {
 function drupal_uninstall_modules($module_list = array(), $uninstall_dependents = TRUE) {
   if ($uninstall_dependents) {
   if ($uninstall_dependents) {

+ 1 - 1
includes/language.inc

@@ -297,7 +297,7 @@ function language_negotiation_get_switch_links($type, $path) {
       // Add support for WCAG 2.0's Language of Parts to add language identifiers.
       // Add support for WCAG 2.0's Language of Parts to add language identifiers.
       // http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html
       // http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html
       foreach ($result as $langcode => $link) {
       foreach ($result as $langcode => $link) {
-        $result[$langcode]['attributes']['lang'] = $langcode;
+        $result[$langcode]['attributes']['xml:lang'] = $langcode;
       }
       }
 
 
       if (!empty($result)) {
       if (!empty($result)) {

+ 57 - 7
includes/locale.inc

@@ -435,6 +435,13 @@ function locale_language_url_rewrite_url(&$path, &$options) {
     switch (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX)) {
     switch (variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX)) {
       case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN:
       case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN:
         if ($options['language']->domain) {
         if ($options['language']->domain) {
+          // Save the original base URL. If it contains a port, we need to
+          // retain it below.
+          if (!empty($options['base_url'])) {
+            // The colon in the URL scheme messes up the port checking below.
+            $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
+          }
+
           // Ask for an absolute URL with our modified base_url.
           // Ask for an absolute URL with our modified base_url.
           global $is_https;
           global $is_https;
           $url_scheme = ($is_https) ? 'https://' : 'http://';
           $url_scheme = ($is_https) ? 'https://' : 'http://';
@@ -449,6 +456,19 @@ function locale_language_url_rewrite_url(&$path, &$options) {
 
 
           // Apply the appropriate protocol to the URL.
           // Apply the appropriate protocol to the URL.
           $options['base_url'] = $url_scheme . $host;
           $options['base_url'] = $url_scheme . $host;
+
+          // In case either the original base URL or the HTTP host contains a
+          // port, retain it.
+          $http_host = $_SERVER['HTTP_HOST'];
+          if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
+            list($host, $port) = explode(':', $normalized_base_url);
+            $options['base_url'] .= ':' . $port;
+          }
+          elseif (strpos($http_host, ':') !== FALSE) {
+            list($host, $port) = explode(':', $http_host);
+            $options['base_url'] .= ':' . $port;
+          }
+
           if (isset($options['https']) && variable_get('https', FALSE)) {
           if (isset($options['https']) && variable_get('https', FALSE)) {
             if ($options['https'] === TRUE) {
             if ($options['https'] === TRUE) {
               $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
               $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
@@ -523,6 +543,22 @@ function locale_language_url_rewrite_session(&$path, &$options) {
  * possible attack vector (img).
  * possible attack vector (img).
  */
  */
 function locale_string_is_safe($string) {
 function locale_string_is_safe($string) {
+  // Some strings have tokens in them. For tokens in the first part of href or
+  // src HTML attributes, filter_xss() removes part of the token, the part
+  // before the first colon.  filter_xss() assumes it could be an attempt to
+  // inject javascript. When filter_xss() removes part of tokens, it causes the
+  // string to not be translatable when it should be translatable. See
+  // LocaleStringIsSafeTest::testLocaleStringIsSafe().
+  //
+  // We can recognize tokens since they are wrapped with brackets and are only
+  // composed of alphanumeric characters, colon, underscore, and dashes. We can
+  // be sure these strings are safe to strip out before the string is checked in
+  // filter_xss() because no dangerous javascript will match that pattern.
+  //
+  // @todo Do not strip out the token. Fix filter_xss() to not incorrectly
+  //   alter the string. https://www.drupal.org/node/2372127
+  $string = preg_replace('/\[[a-z0-9_-]+(:[a-z0-9_-]+)+\]/i', '', $string);
+
   return decode_entities($string) == decode_entities(filter_xss($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
   return decode_entities($string) == decode_entities(filter_xss($string, array('a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var')));
 }
 }
 
 
@@ -631,9 +667,6 @@ function locale_add_language($langcode, $name = NULL, $native = NULL, $direction
  *   translations).
  *   translations).
  */
  */
 function _locale_import_po($file, $langcode, $mode, $group = NULL) {
 function _locale_import_po($file, $langcode, $mode, $group = NULL) {
-  // Try to allocate enough time to parse and import the data.
-  drupal_set_time_limit(240);
-
   // Check if we have the language already in the database.
   // Check if we have the language already in the database.
   if (!db_query("SELECT COUNT(language) FROM {languages} WHERE language = :language", array(':language' => $langcode))->fetchField()) {
   if (!db_query("SELECT COUNT(language) FROM {languages} WHERE language = :language", array(':language' => $langcode))->fetchField()) {
     drupal_set_message(t('The language selected for import is not supported.'), 'error');
     drupal_set_message(t('The language selected for import is not supported.'), 'error');
@@ -717,6 +750,12 @@ function _locale_import_read_po($op, $file, $mode = NULL, $lang = NULL, $group =
   $lineno = 0;
   $lineno = 0;
 
 
   while (!feof($fd)) {
   while (!feof($fd)) {
+    // Refresh the time limit every 10 parsed rows to ensure there is always
+    // enough time to import the data for large PO files.
+    if (!($lineno % 10)) {
+      drupal_set_time_limit(30);
+    }
+
     // A line should not be longer than 10 * 1024.
     // A line should not be longer than 10 * 1024.
     $line = fgets($fd, 10 * 1024);
     $line = fgets($fd, 10 * 1024);
 
 
@@ -1931,7 +1970,7 @@ function _locale_translate_seek() {
       $groups[$string['group']],
       $groups[$string['group']],
       array('data' => check_plain(truncate_utf8($string['source'], 150, FALSE, TRUE)) . '<br /><small>' . $string['location'] . '</small>'),
       array('data' => check_plain(truncate_utf8($string['source'], 150, FALSE, TRUE)) . '<br /><small>' . $string['location'] . '</small>'),
       $string['context'],
       $string['context'],
-      array('data' => _locale_translate_language_list($string['languages'], $limit_language), 'align' => 'center'),
+      array('data' => _locale_translate_language_list($string, $limit_language), 'align' => 'center'),
       array('data' => l(t('edit'), "admin/config/regional/translate/edit/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')),
       array('data' => l(t('edit'), "admin/config/regional/translate/edit/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')),
       array('data' => l(t('delete'), "admin/config/regional/translate/delete/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')),
       array('data' => l(t('delete'), "admin/config/regional/translate/delete/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')),
     );
     );
@@ -2126,16 +2165,21 @@ function _locale_rebuild_js($langcode = NULL) {
 /**
 /**
  * List languages in search result table
  * List languages in search result table
  */
  */
-function _locale_translate_language_list($translation, $limit_language) {
+function _locale_translate_language_list($string, $limit_language) {
   // Add CSS.
   // Add CSS.
   drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css');
   drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css');
 
 
+  // 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();
   $languages = language_list();
-  unset($languages['en']);
+  $default = language_default();
+  $omit = $string['group'] == 'default' ? 'en' : $default->language;
+  unset($languages[$omit]);
   $output = '';
   $output = '';
   foreach ($languages as $langcode => $language) {
   foreach ($languages as $langcode => $language) {
     if (!$limit_language || $limit_language == $langcode) {
     if (!$limit_language || $limit_language == $langcode) {
-      $output .= (!empty($translation[$langcode])) ? $langcode . ' ' : "<em class=\"locale-untranslated\">$langcode</em> ";
+      $output .= (!empty($string['languages'][$langcode])) ? $langcode . ' ' : "<em class=\"locale-untranslated\">$langcode</em> ";
     }
     }
   }
   }
 
 
@@ -2301,6 +2345,8 @@ function _locale_batch_build($files, $finished = NULL, $components = array()) {
 }
 }
 
 
 /**
 /**
+ * Implements callback_batch_operation().
+ *
  * Perform interface translation import as a batch step.
  * Perform interface translation import as a batch step.
  *
  *
  * @param $filepath
  * @param $filepath
@@ -2319,6 +2365,8 @@ function _locale_batch_import($filepath, &$context) {
 }
 }
 
 
 /**
 /**
+ * Implements callback_batch_finished().
+ *
  * Finished callback of system page locale import batch.
  * Finished callback of system page locale import batch.
  * Inform the user of translation files imported.
  * Inform the user of translation files imported.
  */
  */
@@ -2329,6 +2377,8 @@ function _locale_batch_system_finished($success, $results) {
 }
 }
 
 
 /**
 /**
+ * Implements callback_batch_finished().
+ *
  * Finished callback of language addition locale import batch.
  * Finished callback of language addition locale import batch.
  * Inform the user of translation files imported.
  * Inform the user of translation files imported.
  */
  */

+ 1 - 1
includes/mail.inc

@@ -566,7 +566,7 @@ function _drupal_wrap_mail_line(&$line, $key, $values) {
   // Use soft-breaks only for purely quoted or unindented text.
   // Use soft-breaks only for purely quoted or unindented text.
   $line = wordwrap($line, 77 - $values['length'], $values['soft'] ? " \n" : "\n");
   $line = wordwrap($line, 77 - $values['length'], $values['soft'] ? " \n" : "\n");
   // Break really long words at the maximum width allowed.
   // Break really long words at the maximum width allowed.
-  $line = wordwrap($line, 996 - $values['length'], $values['soft'] ? " \n" : "\n");
+  $line = wordwrap($line, 996 - $values['length'], $values['soft'] ? " \n" : "\n", TRUE);
 }
 }
 
 
 /**
 /**

+ 67 - 8
includes/menu.inc

@@ -229,12 +229,20 @@ define('MENU_CONTEXT_INLINE', 0x0002);
 define('MENU_FOUND', 1);
 define('MENU_FOUND', 1);
 
 
 /**
 /**
- * Internal menu status code -- Menu item was not found.
+ * Menu status code -- Not found.
+ *
+ * This can be used as the return value from a page callback, although it is
+ * preferable to use a load function to accomplish this; see the hook_menu()
+ * documentation for details.
  */
  */
 define('MENU_NOT_FOUND', 2);
 define('MENU_NOT_FOUND', 2);
 
 
 /**
 /**
- * Internal menu status code -- Menu item access is denied.
+ * Menu status code -- Access denied.
+ *
+ * This can be used as the return value from a page callback, although it is
+ * preferable to use an access callback to accomplish this; see the hook_menu()
+ * documentation for details.
  */
  */
 define('MENU_ACCESS_DENIED', 3);
 define('MENU_ACCESS_DENIED', 3);
 
 
@@ -431,7 +439,7 @@ function menu_set_item($path, $router_item) {
  *
  *
  * @param $path
  * @param $path
  *   The path; for example, 'node/5'. The function will find the corresponding
  *   The path; for example, 'node/5'. The function will find the corresponding
- *   node/% item and return that.
+ *   node/% item and return that. Defaults to the current path.
  * @param $router_item
  * @param $router_item
  *   Internal use only.
  *   Internal use only.
  *
  *
@@ -456,7 +464,9 @@ function menu_get_item($path = NULL, $router_item = NULL) {
     // Rebuild if we know it's needed, or if the menu masks are missing which
     // Rebuild if we know it's needed, or if the menu masks are missing which
     // occurs rarely, likely due to a race condition of multiple rebuilds.
     // occurs rarely, likely due to a race condition of multiple rebuilds.
     if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
     if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
-      menu_rebuild();
+      if (_menu_check_rebuild()) {
+        menu_rebuild();
+      }
     }
     }
     $original_map = arg(NULL, $path);
     $original_map = arg(NULL, $path);
 
 
@@ -1485,7 +1495,7 @@ function menu_tree_collect_node_links(&$tree, &$node_links) {
  *   menu_tree_collect_node_links().
  *   menu_tree_collect_node_links().
  */
  */
 function menu_tree_check_access(&$tree, $node_links = array()) {
 function menu_tree_check_access(&$tree, $node_links = array()) {
-  if ($node_links) {
+  if ($node_links && (user_access('access content') || user_access('bypass node access'))) {
     $nids = array_keys($node_links);
     $nids = array_keys($node_links);
     $select = db_select('node', 'n');
     $select = db_select('node', 'n');
     $select->addField('n', 'nid');
     $select->addField('n', 'nid');
@@ -2409,7 +2419,7 @@ function menu_set_active_trail($new_trail = NULL) {
           // argument placeholders (%). Such links are not contained in regular
           // argument placeholders (%). Such links are not contained in regular
           // menu trees, and have only been loaded for the additional
           // menu trees, and have only been loaded for the additional
           // translation that happens here, so as to be able to display them in
           // translation that happens here, so as to be able to display them in
-          // the breadcumb for the current page.
+          // the breadcrumb for the current page.
           // @see _menu_tree_check_access()
           // @see _menu_tree_check_access()
           // @see _menu_link_translate()
           // @see _menu_link_translate()
           if (strpos($link['href'], '%') !== FALSE) {
           if (strpos($link['href'], '%') !== FALSE) {
@@ -2611,10 +2621,30 @@ function menu_get_active_breadcrumb() {
  */
  */
 function menu_get_active_title() {
 function menu_get_active_title() {
   $active_trail = menu_get_active_trail();
   $active_trail = menu_get_active_trail();
+  $local_task_title = NULL;
 
 
   foreach (array_reverse($active_trail) as $item) {
   foreach (array_reverse($active_trail) as $item) {
-    if (!(bool) ($item['type'] & MENU_IS_LOCAL_TASK)) {
-      return $item['title'];
+    // Local task titles are displayed as tabs and therefore should not be
+    // repeated as the page title. However, if the local task appears in a
+    // top-level menu, it is no longer a "local task" anymore (the front page
+    // of the site does not have tabs) so it is better to use the local task
+    // title in that case than to fall back on the front page link in the
+    // active trail (which is usually "Home" and would not make sense in this
+    // context).
+    if ((bool) ($item['type'] & MENU_IS_LOCAL_TASK)) {
+      // A local task title is being skipped; track it in case it needs to be
+      // used later.
+      $local_task_title = $item['title'];
+    }
+    else {
+      // This is not a local task, so use it for the page title (unless the
+      // conditions described above are met).
+      if (isset($local_task_title) && isset($item['href']) && $item['href'] == '<front>') {
+        return $local_task_title;
+      }
+      else {
+        return $item['title'];
+      }
     }
     }
   }
   }
 }
 }
@@ -2693,6 +2723,21 @@ function menu_reset_static_cache() {
   drupal_static_reset('menu_link_get_preferred');
   drupal_static_reset('menu_link_get_preferred');
 }
 }
 
 
+/**
+ * Checks whether a menu_rebuild() is necessary.
+ */
+function _menu_check_rebuild() {
+  // To absolutely ensure that the menu rebuild is required, re-load the
+  // variables in case they were set by another process.
+  $variables = variable_initialize();
+  if (empty($variables['menu_rebuild_needed']) && !empty($variables['menu_masks'])) {
+    unset($GLOBALS['conf']['menu_rebuild_needed']);
+    $GLOBALS['conf']['menu_masks'] = $variables['menu_masks'];
+    return FALSE;
+  }
+  return TRUE;
+}
+
 /**
 /**
  * Populates the database tables used by various menu functions.
  * Populates the database tables used by various menu functions.
  *
  *
@@ -2713,6 +2758,14 @@ function menu_rebuild() {
     // We choose to block here since otherwise the router item may not
     // We choose to block here since otherwise the router item may not
     // be available in menu_execute_active_handler() resulting in a 404.
     // be available in menu_execute_active_handler() resulting in a 404.
     lock_wait('menu_rebuild');
     lock_wait('menu_rebuild');
+
+    if (_menu_check_rebuild()) {
+      // If we get here and menu_masks was not set, then it is possible a menu
+      // is being reloaded, or that the process rebuilding the menu was unable
+      // to complete successfully. A missing menu_masks variable could result
+      // in a 404, so re-run the function.
+      return menu_rebuild();
+    }
     return FALSE;
     return FALSE;
   }
   }
 
 
@@ -2737,6 +2790,12 @@ function menu_rebuild() {
     $transaction->rollback();
     $transaction->rollback();
     watchdog_exception('menu', $e);
     watchdog_exception('menu', $e);
   }
   }
+  // Explicitly commit the transaction now; this ensures that the database
+  // operations during the menu rebuild are committed before the lock is made
+  // available again, since locks may not always reside in the same database
+  // connection. The lock is acquired outside of the transaction so should also
+  // be released outside of it.
+  unset($transaction);
 
 
   lock_release('menu_rebuild');
   lock_release('menu_rebuild');
   return TRUE;
   return TRUE;

+ 76 - 14
includes/module.inc

@@ -227,6 +227,10 @@ function system_list_reset() {
   drupal_static_reset('list_themes');
   drupal_static_reset('list_themes');
   cache_clear_all('bootstrap_modules', 'cache_bootstrap');
   cache_clear_all('bootstrap_modules', 'cache_bootstrap');
   cache_clear_all('system_list', 'cache_bootstrap');
   cache_clear_all('system_list', 'cache_bootstrap');
+
+  // Clean up the bootstrap file scan cache.
+  drupal_static_reset('_drupal_file_scan_cache');
+  cache_clear_all('_drupal_file_scan_cache', 'cache_bootstrap');
 }
 }
 
 
 /**
 /**
@@ -265,11 +269,11 @@ function _module_build_dependencies($files) {
 /**
 /**
  * Determines whether a given module exists.
  * Determines whether a given module exists.
  *
  *
- * @param $module
+ * @param string $module
  *   The name of the module (without the .module extension).
  *   The name of the module (without the .module extension).
  *
  *
- * @return
- *   TRUE if the module is both installed and enabled.
+ * @return bool
+ *   TRUE if the module is both installed and enabled, FALSE otherwise.
  */
  */
 function module_exists($module) {
 function module_exists($module) {
   $list = module_list();
   $list = module_list();
@@ -320,16 +324,27 @@ function module_load_install($module) {
  *   The name of the included file, if successful; FALSE otherwise.
  *   The name of the included file, if successful; FALSE otherwise.
  */
  */
 function module_load_include($type, $module, $name = NULL) {
 function module_load_include($type, $module, $name = NULL) {
+  static $files = array();
+
   if (!isset($name)) {
   if (!isset($name)) {
     $name = $module;
     $name = $module;
   }
   }
 
 
+  $key = $type . ':' . $module . ':' . $name;
+  if (isset($files[$key])) {
+    return $files[$key];
+  }
+
   if (function_exists('drupal_get_path')) {
   if (function_exists('drupal_get_path')) {
     $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
     $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
     if (is_file($file)) {
     if (is_file($file)) {
       require_once $file;
       require_once $file;
+      $files[$key] = $file;
       return $file;
       return $file;
     }
     }
+    else {
+      $files[$key] = FALSE;
+    }
   }
   }
   return FALSE;
   return FALSE;
 }
 }
@@ -365,20 +380,22 @@ function module_load_all_includes($type, $name = NULL) {
  * - Invoke hook_modules_installed().
  * - Invoke hook_modules_installed().
  * - Invoke hook_modules_enabled().
  * - Invoke hook_modules_enabled().
  *
  *
- * @param $module_list
+ * @param string[] $module_list
  *   An array of module names.
  *   An array of module names.
- * @param $enable_dependencies
+ * @param bool $enable_dependencies
  *   If TRUE, dependencies will automatically be added and enabled in the
  *   If TRUE, dependencies will automatically be added and enabled in the
  *   correct order. This incurs a significant performance cost, so use FALSE
  *   correct order. This incurs a significant performance cost, so use FALSE
  *   if you know $module_list is already complete and in the correct order.
  *   if you know $module_list is already complete and in the correct order.
  *
  *
- * @return
+ * @return bool
  *   FALSE if one or more dependencies are missing, TRUE otherwise.
  *   FALSE if one or more dependencies are missing, TRUE otherwise.
  *
  *
  * @see hook_install()
  * @see hook_install()
  * @see hook_enable()
  * @see hook_enable()
  * @see hook_modules_installed()
  * @see hook_modules_installed()
  * @see hook_modules_enabled()
  * @see hook_modules_enabled()
+ * @see module_disable()
+ * @see drupal_uninstall_modules()
  */
  */
 function module_enable($module_list, $enable_dependencies = TRUE) {
 function module_enable($module_list, $enable_dependencies = TRUE) {
   if ($enable_dependencies) {
   if ($enable_dependencies) {
@@ -505,12 +522,15 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
 /**
 /**
  * Disables a given set of modules.
  * Disables a given set of modules.
  *
  *
- * @param $module_list
+ * @param string[] $module_list
  *   An array of module names.
  *   An array of module names.
- * @param $disable_dependents
+ * @param bool $disable_dependents
  *   If TRUE, dependent modules will automatically be added and disabled in the
  *   If TRUE, dependent modules will automatically be added and disabled in the
  *   correct order. This incurs a significant performance cost, so use FALSE
  *   correct order. This incurs a significant performance cost, so use FALSE
  *   if you know $module_list is already complete and in the correct order.
  *   if you know $module_list is already complete and in the correct order.
+ *
+ * @see drupal_uninstall_modules()
+ * @see module_enable()
  */
  */
 function module_disable($module_list, $disable_dependents = TRUE) {
 function module_disable($module_list, $disable_dependents = TRUE) {
   if ($disable_dependents) {
   if ($disable_dependents) {
@@ -676,12 +696,16 @@ function module_hook($module, $hook) {
 /**
 /**
  * Determines which modules are implementing a hook.
  * Determines which modules are implementing a hook.
  *
  *
- * @param $hook
+ * Lazy-loaded include files specified with "group" via hook_hook_info() or
+ * hook_module_implements_alter() will be automatically included by this
+ * function when necessary.
+ *
+ * @param string $hook
  *   The name of the hook (e.g. "help" or "menu").
  *   The name of the hook (e.g. "help" or "menu").
- * @param $sort
+ * @param bool $sort
  *   By default, modules are ordered by weight and filename, settings this option
  *   By default, modules are ordered by weight and filename, settings this option
  *   to TRUE, module list will be ordered by module name.
  *   to TRUE, module list will be ordered by module name.
- * @param $reset
+ * @param bool $reset
  *   For internal use only: Whether to force the stored list of hook
  *   For internal use only: Whether to force the stored list of hook
  *   implementations to be regenerated (such as after enabling a new module,
  *   implementations to be regenerated (such as after enabling a new module,
  *   before processing hook_enable).
  *   before processing hook_enable).
@@ -696,8 +720,10 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
   static $drupal_static_fast;
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
   if (!isset($drupal_static_fast)) {
     $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__);
     $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__);
+    $drupal_static_fast['verified'] = &drupal_static(__FUNCTION__ . ':verified');
   }
   }
   $implementations = &$drupal_static_fast['implementations'];
   $implementations = &$drupal_static_fast['implementations'];
+  $verified = &$drupal_static_fast['verified'];
 
 
   // We maintain a persistent cache of hook implementations in addition to the
   // We maintain a persistent cache of hook implementations in addition to the
   // static cache to avoid looping through every module and every hook on each
   // static cache to avoid looping through every module and every hook on each
@@ -711,14 +737,19 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
   // per request.
   // per request.
   if ($reset) {
   if ($reset) {
     $implementations = array();
     $implementations = array();
+    $verified = array();
     cache_set('module_implements', array(), 'cache_bootstrap');
     cache_set('module_implements', array(), 'cache_bootstrap');
     drupal_static_reset('module_hook_info');
     drupal_static_reset('module_hook_info');
     drupal_static_reset('drupal_alter');
     drupal_static_reset('drupal_alter');
     cache_clear_all('hook_info', 'cache_bootstrap');
     cache_clear_all('hook_info', 'cache_bootstrap');
+    cache_clear_all('system_cache_tables', 'cache');
     return;
     return;
   }
   }
 
 
   // Fetch implementations from cache.
   // Fetch implementations from cache.
+  // This happens on the first call to module_implements(*, *, FALSE) during a
+  // request, but also when $implementations have been reset, e.g. after
+  // module_enable().
   if (empty($implementations)) {
   if (empty($implementations)) {
     $implementations = cache_get('module_implements', 'cache_bootstrap');
     $implementations = cache_get('module_implements', 'cache_bootstrap');
     if ($implementations === FALSE) {
     if ($implementations === FALSE) {
@@ -727,12 +758,17 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
     else {
     else {
       $implementations = $implementations->data;
       $implementations = $implementations->data;
     }
     }
+    // Forget all previously "verified" hooks, in case that $implementations
+    // were cleared via drupal_static_reset('module_implements') instead of
+    // module_implements(*, *, TRUE).
+    $verified = array();
   }
   }
 
 
   if (!isset($implementations[$hook])) {
   if (!isset($implementations[$hook])) {
     // The hook is not cached, so ensure that whether or not it has
     // The hook is not cached, so ensure that whether or not it has
     // implementations, that the cache is updated at the end of the request.
     // implementations, that the cache is updated at the end of the request.
     $implementations['#write_cache'] = TRUE;
     $implementations['#write_cache'] = TRUE;
+    // Discover implementations for this hook.
     $hook_info = module_hook_info();
     $hook_info = module_hook_info();
     $implementations[$hook] = array();
     $implementations[$hook] = array();
     $list = module_list(FALSE, FALSE, $sort);
     $list = module_list(FALSE, FALSE, $sort);
@@ -744,13 +780,31 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
         $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
         $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
       }
       }
     }
     }
-    // Allow modules to change the weight of specific implementations but avoid
+    // Allow modules to change the weight of specific implementations, but avoid
     // an infinite loop.
     // an infinite loop.
     if ($hook != 'module_implements_alter') {
     if ($hook != 'module_implements_alter') {
+      // Remember the implementations before hook_module_implements_alter().
+      $implementations_before = $implementations[$hook];
       drupal_alter('module_implements', $implementations[$hook], $hook);
       drupal_alter('module_implements', $implementations[$hook], $hook);
+      // Verify implementations that were added or modified.
+      foreach (array_diff_assoc($implementations[$hook], $implementations_before) as $module => $group) {
+        // If drupal_alter('module_implements') changed or added a $group, the
+        // respective file needs to be included.
+        if ($group) {
+          module_load_include('inc', $module, "$module.$group");
+        }
+        // If a new implementation was added, verify that the function exists.
+        if (!function_exists($module . '_' . $hook)) {
+          unset($implementations[$hook][$module]);
+        }
+      }
     }
     }
+    // Implementations for this hook are now "verified".
+    $verified[$hook] = TRUE;
   }
   }
-  else {
+  elseif (!isset($verified[$hook])) {
+    // Implementations for this hook were in the cache, but they are not
+    // "verified" yet.
     foreach ($implementations[$hook] as $module => $group) {
     foreach ($implementations[$hook] as $module => $group) {
       // If this hook implementation is stored in a lazy-loaded file, so include
       // If this hook implementation is stored in a lazy-loaded file, so include
       // that file first.
       // that file first.
@@ -769,6 +823,7 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) {
         $implementations['#write_cache'] = TRUE;
         $implementations['#write_cache'] = TRUE;
       }
       }
     }
     }
+    $verified[$hook] = TRUE;
   }
   }
 
 
   return array_keys($implementations[$hook]);
   return array_keys($implementations[$hook]);
@@ -833,6 +888,11 @@ function module_hook_info() {
  * @see module_implements()
  * @see module_implements()
  */
  */
 function module_implements_write_cache() {
 function module_implements_write_cache() {
+  // The list of implementations includes vital modules only before full
+  // bootstrap, so do not write cache if we are not fully bootstrapped yet.
+  if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) {
+    return;
+  }
   $implementations = &drupal_static('module_implements');
   $implementations = &drupal_static('module_implements');
   if (isset($implementations['#write_cache'])) {
   if (isset($implementations['#write_cache'])) {
     unset($implementations['#write_cache']);
     unset($implementations['#write_cache']);
@@ -880,7 +940,9 @@ function module_invoke($module, $hook) {
  *
  *
  * @return
  * @return
  *   An array of return values of the hook implementations. If modules return
  *   An array of return values of the hook implementations. If modules return
- *   arrays from their implementations, those are merged into one array.
+ *   arrays from their implementations, those are merged into one array
+ *   recursively. Note: integer keys in arrays will be lost, as the merge is
+ *   done using array_merge_recursive().
  *
  *
  * @see drupal_alter()
  * @see drupal_alter()
  */
  */

+ 2 - 1
includes/path.inc

@@ -347,7 +347,8 @@ function drupal_match_path($path, $patterns) {
  * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available.
  * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available.
  *
  *
  * @return
  * @return
- *   The current Drupal URL path.
+ *   The current Drupal URL path. The path is untrusted user input and must be
+ *   treated as such.
  *
  *
  * @see request_path()
  * @see request_path()
  */
  */

+ 1 - 1
includes/registry.inc

@@ -164,7 +164,7 @@ function _registry_parse_files($files) {
  *   (optional) Weight of the module.
  *   (optional) Weight of the module.
  */
  */
 function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
 function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
-  if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
+  if (preg_match_all('/^\s*(?:abstract|final)?\s*(class|interface|trait)\s+([a-zA-Z0-9_]+)/m', $contents, $matches)) {
     foreach ($matches[2] as $key => $name) {
     foreach ($matches[2] as $key => $name) {
       db_merge('registry')
       db_merge('registry')
         ->key(array(
         ->key(array(

+ 4 - 2
includes/session.inc

@@ -163,7 +163,7 @@ function _drupal_session_write($sid, $value) {
   try {
   try {
     if (!drupal_save_session()) {
     if (!drupal_save_session()) {
       // We don't have anything to do if we are not allowed to save the session.
       // We don't have anything to do if we are not allowed to save the session.
-      return;
+      return TRUE;
     }
     }
 
 
     // Check whether $_SESSION has been changed in this request.
     // Check whether $_SESSION has been changed in this request.
@@ -425,7 +425,7 @@ function _drupal_session_destroy($sid) {
 
 
   // Nothing to do if we are not allowed to change the session.
   // Nothing to do if we are not allowed to change the session.
   if (!drupal_save_session()) {
   if (!drupal_save_session()) {
-    return;
+    return TRUE;
   }
   }
 
 
   // Delete session data.
   // Delete session data.
@@ -446,6 +446,8 @@ function _drupal_session_destroy($sid) {
   elseif (variable_get('https', FALSE)) {
   elseif (variable_get('https', FALSE)) {
     _drupal_session_delete_cookie('S' . session_name(), TRUE);
     _drupal_session_delete_cookie('S' . session_name(), TRUE);
   }
   }
+
+  return TRUE;
 }
 }
 
 
 /**
 /**

+ 32 - 8
includes/theme.inc

@@ -1248,6 +1248,7 @@ function path_to_theme() {
 function drupal_find_theme_functions($cache, $prefixes) {
 function drupal_find_theme_functions($cache, $prefixes) {
   $implementations = array();
   $implementations = array();
   $functions = get_defined_functions();
   $functions = get_defined_functions();
+  $theme_functions = preg_grep('/^(' . implode(')|(', $prefixes) . ')_/', $functions['user']);
 
 
   foreach ($cache as $hook => $info) {
   foreach ($cache as $hook => $info) {
     foreach ($prefixes as $prefix) {
     foreach ($prefixes as $prefix) {
@@ -1264,7 +1265,7 @@ function drupal_find_theme_functions($cache, $prefixes) {
       // intermediary suggestion.
       // intermediary suggestion.
       $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
       $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
       if (!isset($info['base hook']) && !empty($pattern)) {
       if (!isset($info['base hook']) && !empty($pattern)) {
-        $matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $functions['user']);
+        $matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $theme_functions);
         if ($matches) {
         if ($matches) {
           foreach ($matches as $match) {
           foreach ($matches as $match) {
             $new_hook = substr($match, strlen($prefix) + 1);
             $new_hook = substr($match, strlen($prefix) + 1);
@@ -1691,7 +1692,7 @@ function theme_status_messages($variables) {
       $output .= " </ul>\n";
       $output .= " </ul>\n";
     }
     }
     else {
     else {
-      $output .= $messages[0];
+      $output .= reset($messages);
     }
     }
     $output .= "</div>\n";
     $output .= "</div>\n";
   }
   }
@@ -1710,11 +1711,29 @@ function theme_status_messages($variables) {
  * copy if none of the enabled modules or the active theme implement any
  * copy if none of the enabled modules or the active theme implement any
  * preprocess or process functions or override this theme implementation.
  * preprocess or process functions or override this theme implementation.
  *
  *
- * @param $variables
- *   An associative array containing the keys 'text', 'path', and 'options'.
- *   See the l() function for information about these variables.
+ * @param array $variables
+ *   An associative array containing the keys:
+ *   - text: The text of the link.
+ *   - path: The internal path or external URL being linked to. It is used as
+ *     the $path parameter of the url() function.
+ *   - options: (optional) An array that defaults to empty, but can contain:
+ *     - attributes: Can contain optional attributes:
+ *       - class: must be declared in an array. Example: 'class' =>
+ *         array('class_name1','class_name2').
+ *       - title: must be a string. Example: 'title' => 'Example title'
+ *       - Others are more flexible as long as they work with
+ *         drupal_attributes($variables['options']['attributes]).
+ *     - html: Boolean flag that tells whether text contains HTML or plain
+ *       text. If set to TRUE, the text value will not be sanitized so the
+         calling function must ensure that it already contains safe HTML.
+ *   The elements $variables['options']['attributes'] and
+ *   $variables['options']['html'] are used in this function similarly to the
+ *   way that $options['attributes'] and $options['html'] are used in l().
+ *   The link itself is built by the url() function, which takes
+ *   $variables['path'] and $variables['options'] as arguments.
  *
  *
  * @see l()
  * @see l()
+ * @see url()
  */
  */
 function theme_link($variables) {
 function theme_link($variables) {
   return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
   return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
@@ -1791,7 +1810,8 @@ function theme_links($variables) {
     foreach ($links as $key => $link) {
     foreach ($links as $key => $link) {
       $class = array($key);
       $class = array($key);
 
 
-      // Add first, last and active classes to the list of links to help out themers.
+      // Add first, last and active classes to the list of links to help out
+      // themers.
       if ($i == 1) {
       if ($i == 1) {
         $class[] = 'first';
         $class[] = 'first';
       }
       }
@@ -1809,7 +1829,8 @@ function theme_links($variables) {
         $output .= l($link['title'], $link['href'], $link);
         $output .= l($link['title'], $link['href'], $link);
       }
       }
       elseif (!empty($link['title'])) {
       elseif (!empty($link['title'])) {
-        // Some links are actually not links, but we wrap these in <span> for adding title and class attributes.
+        // Some links are actually not links, but we wrap these in <span> for
+        // adding title and class attributes.
         if (empty($link['html'])) {
         if (empty($link['html'])) {
           $link['title'] = check_plain($link['title']);
           $link['title'] = check_plain($link['title']);
         }
         }
@@ -2618,10 +2639,13 @@ function template_preprocess_page(&$variables) {
   // Move some variables to the top level for themer convenience and template cleanliness.
   // Move some variables to the top level for themer convenience and template cleanliness.
   $variables['show_messages'] = $variables['page']['#show_messages'];
   $variables['show_messages'] = $variables['page']['#show_messages'];
 
 
-  foreach (system_region_list($GLOBALS['theme']) as $region_key => $region_name) {
+  foreach (system_region_list($GLOBALS['theme'], REGIONS_ALL, FALSE) as $region_key) {
     if (!isset($variables['page'][$region_key])) {
     if (!isset($variables['page'][$region_key])) {
       $variables['page'][$region_key] = array();
       $variables['page'][$region_key] = array();
     }
     }
+    if ($region_content = drupal_get_region_content($region_key)) {
+      $variables['page'][$region_key][]['#markup'] = $region_content;
+    }
   }
   }
 
 
   // Set up layout variable.
   // Set up layout variable.

+ 12 - 0
includes/update.inc

@@ -795,6 +795,14 @@ function update_fix_d7_requirements() {
 function update_fix_d7_install_profile() {
 function update_fix_d7_install_profile() {
   $profile = drupal_get_profile();
   $profile = drupal_get_profile();
 
 
+  // 'Default' profile has been renamed to 'Standard' in D7.
+  // We change the profile here to prevent a broken record in the system table.
+  // See system_update_7049().
+  if ($profile == 'default') {
+    $profile = 'standard';
+    variable_set('install_profile', $profile);
+  }
+
   $results = db_select('system', 's')
   $results = db_select('system', 's')
     ->fields('s', array('name', 'schema_version'))
     ->fields('s', array('name', 'schema_version'))
     ->condition('name', $profile)
     ->condition('name', $profile)
@@ -908,6 +916,8 @@ function update_get_d6_session_name() {
 }
 }
 
 
 /**
 /**
+ * Implements callback_batch_operation().
+ *
  * Performs one update and stores the results for display on the results page.
  * Performs one update and stores the results for display on the results page.
  *
  *
  * If an update function completes successfully, it should return a message
  * If an update function completes successfully, it should return a message
@@ -1078,6 +1088,8 @@ function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $
 }
 }
 
 
 /**
 /**
+ * Implements callback_batch_finished().
+ *
  * Finishes the update process and stores the results for eventual display.
  * Finishes the update process and stores the results for eventual display.
  *
  *
  * After the updates run, all caches are flushed. The update results are
  * After the updates run, all caches are flushed. The update results are

+ 8 - 0
includes/xmlrpcs.inc

@@ -264,6 +264,10 @@ function xmlrpc_server_call($xmlrpc_server, $methodname, $args) {
  */
  */
 function xmlrpc_server_multicall($methodcalls) {
 function xmlrpc_server_multicall($methodcalls) {
   // See http://www.xmlrpc.com/discuss/msgReader$1208
   // See http://www.xmlrpc.com/discuss/msgReader$1208
+  // To avoid multicall expansion attacks, limit the number of duplicate method
+  // calls allowed with a default of 1. Set to -1 for unlimited.
+  $duplicate_method_limit = variable_get('xmlrpc_multicall_duplicate_method_limit', 1);
+  $method_count = array();
   $return = array();
   $return = array();
   $xmlrpc_server = xmlrpc_server_get();
   $xmlrpc_server = xmlrpc_server_get();
   foreach ($methodcalls as $call) {
   foreach ($methodcalls as $call) {
@@ -273,10 +277,14 @@ function xmlrpc_server_multicall($methodcalls) {
       $ok = FALSE;
       $ok = FALSE;
     }
     }
     $method = $call['methodName'];
     $method = $call['methodName'];
+    $method_count[$method] = isset($method_count[$method]) ? $method_count[$method] + 1 : 1;
     $params = $call['params'];
     $params = $call['params'];
     if ($method == 'system.multicall') {
     if ($method == 'system.multicall') {
       $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden.'));
       $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden.'));
     }
     }
+    elseif ($duplicate_method_limit > 0 && $method_count[$method] > $duplicate_method_limit) {
+      $result = xmlrpc_error(-156579, t('Too many duplicate method calls in system.multicall.'));
+    }
     elseif ($ok) {
     elseif ($ok) {
       $result = xmlrpc_server_call($xmlrpc_server, $method, $params);
       $result = xmlrpc_server_call($xmlrpc_server, $method, $params);
     }
     }

+ 34 - 6
misc/ajax.js

@@ -14,6 +14,8 @@
 
 
 Drupal.ajax = Drupal.ajax || {};
 Drupal.ajax = Drupal.ajax || {};
 
 
+Drupal.settings.urlIsAjaxTrusted = Drupal.settings.urlIsAjaxTrusted || {};
+
 /**
 /**
  * Attaches the Ajax behavior to each Ajax form element.
  * Attaches the Ajax behavior to each Ajax form element.
  */
  */
@@ -130,6 +132,11 @@ Drupal.ajax = function (base, element, element_settings) {
   // 5. /nojs# - Followed by a fragment.
   // 5. /nojs# - Followed by a fragment.
   //      E.g.: path/nojs#myfragment
   //      E.g.: path/nojs#myfragment
   this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
   this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
+  // If the 'nojs' version of the URL is trusted, also trust the 'ajax' version.
+  if (Drupal.settings.urlIsAjaxTrusted[element_settings.url]) {
+    Drupal.settings.urlIsAjaxTrusted[this.url] = true;
+  }
+
   this.wrapper = '#' + element_settings.wrapper;
   this.wrapper = '#' + element_settings.wrapper;
 
 
   // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
   // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
@@ -155,18 +162,36 @@ Drupal.ajax = function (base, element, element_settings) {
       ajax.ajaxing = true;
       ajax.ajaxing = true;
       return ajax.beforeSend(xmlhttprequest, options);
       return ajax.beforeSend(xmlhttprequest, options);
     },
     },
-    success: function (response, status) {
+    success: function (response, status, xmlhttprequest) {
       // Sanity check for browser support (object expected).
       // Sanity check for browser support (object expected).
       // When using iFrame uploads, responses must be returned as a string.
       // When using iFrame uploads, responses must be returned as a string.
       if (typeof response == 'string') {
       if (typeof response == 'string') {
         response = $.parseJSON(response);
         response = $.parseJSON(response);
       }
       }
+
+      // Prior to invoking the response's commands, verify that they can be
+      // trusted by checking for a response header. See
+      // ajax_set_verification_header() for details.
+      // - Empty responses are harmless so can bypass verification. This avoids
+      //   an alert message for server-generated no-op responses that skip Ajax
+      //   rendering.
+      // - Ajax objects with trusted URLs (e.g., ones defined server-side via
+      //   #ajax) can bypass header verification. This is especially useful for
+      //   Ajax with multipart forms. Because IFRAME transport is used, the
+      //   response headers cannot be accessed for verification.
+      if (response !== null && !Drupal.settings.urlIsAjaxTrusted[ajax.url]) {
+        if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
+          var customMessage = Drupal.t("The response failed verification so will not be processed.");
+          return ajax.error(xmlhttprequest, ajax.url, customMessage);
+        }
+      }
+
       return ajax.success(response, status);
       return ajax.success(response, status);
     },
     },
-    complete: function (response, status) {
+    complete: function (xmlhttprequest, status) {
       ajax.ajaxing = false;
       ajax.ajaxing = false;
       if (status == 'error' || status == 'parsererror') {
       if (status == 'error' || status == 'parsererror') {
-        return ajax.error(response, ajax.url);
+        return ajax.error(xmlhttprequest, ajax.url);
       }
       }
     },
     },
     dataType: 'json',
     dataType: 'json',
@@ -175,6 +200,9 @@ Drupal.ajax = function (base, element, element_settings) {
 
 
   // Bind the ajaxSubmit function to the element event.
   // Bind the ajaxSubmit function to the element event.
   $(ajax.element).bind(element_settings.event, function (event) {
   $(ajax.element).bind(element_settings.event, function (event) {
+    if (!Drupal.settings.urlIsAjaxTrusted[ajax.url] && !Drupal.urlIsLocal(ajax.url)) {
+      throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', {'!url': ajax.url}));
+    }
     return ajax.eventResponse(this, event);
     return ajax.eventResponse(this, event);
   });
   });
 
 
@@ -447,8 +475,8 @@ Drupal.ajax.prototype.getEffect = function (response) {
 /**
 /**
  * Handler for the form redirection error.
  * Handler for the form redirection error.
  */
  */
-Drupal.ajax.prototype.error = function (response, uri) {
-  alert(Drupal.ajaxError(response, uri));
+Drupal.ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
+  Drupal.displayAjaxError(Drupal.ajaxError(xmlhttprequest, uri, customMessage));
   // Remove the progress element.
   // Remove the progress element.
   if (this.progress.element) {
   if (this.progress.element) {
     $(this.progress.element).remove();
     $(this.progress.element).remove();
@@ -462,7 +490,7 @@ Drupal.ajax.prototype.error = function (response, uri) {
   $(this.element).removeClass('progress-disabled').removeAttr('disabled');
   $(this.element).removeClass('progress-disabled').removeAttr('disabled');
   // Reattach behaviors, if they were detached in beforeSerialize().
   // Reattach behaviors, if they were detached in beforeSerialize().
   if (this.form) {
   if (this.form) {
-    var settings = response.settings || this.settings || Drupal.settings;
+    var settings = this.settings || Drupal.settings;
     Drupal.attachBehaviors(this.form, settings);
     Drupal.attachBehaviors(this.form, settings);
   }
   }
 };
 };

+ 9 - 5
misc/autocomplete.js

@@ -114,6 +114,7 @@ Drupal.jsAC.prototype.onkeyup = function (input, e) {
  */
  */
 Drupal.jsAC.prototype.select = function (node) {
 Drupal.jsAC.prototype.select = function (node) {
   this.input.value = $(node).data('autocompleteValue');
   this.input.value = $(node).data('autocompleteValue');
+  $(this.input).trigger('autocompleteSelect', [node]);
 };
 };
 
 
 /**
 /**
@@ -167,7 +168,7 @@ Drupal.jsAC.prototype.unhighlight = function (node) {
 Drupal.jsAC.prototype.hidePopup = function (keycode) {
 Drupal.jsAC.prototype.hidePopup = function (keycode) {
   // Select item if the right key or mousebutton was pressed.
   // Select item if the right key or mousebutton was pressed.
   if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
   if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
-    this.input.value = $(this.selected).data('autocompleteValue');
+    this.select(this.selected);
   }
   }
   // Hide popup.
   // Hide popup.
   var popup = this.popup;
   var popup = this.popup;
@@ -220,7 +221,7 @@ Drupal.jsAC.prototype.found = function (matches) {
   for (key in matches) {
   for (key in matches) {
     $('<li></li>')
     $('<li></li>')
       .html($('<div></div>').html(matches[key]))
       .html($('<div></div>').html(matches[key]))
-      .mousedown(function () { ac.select(this); })
+      .mousedown(function () { ac.hidePopup(this); })
       .mouseover(function () { ac.highlight(this); })
       .mouseover(function () { ac.highlight(this); })
       .mouseout(function () { ac.unhighlight(this); })
       .mouseout(function () { ac.unhighlight(this); })
       .data('autocompleteValue', key)
       .data('autocompleteValue', key)
@@ -270,8 +271,11 @@ Drupal.ACDB.prototype.search = function (searchString) {
   var db = this;
   var db = this;
   this.searchString = searchString;
   this.searchString = searchString;
 
 
-  // See if this string needs to be searched for anyway.
-  searchString = searchString.replace(/^\s+|\s+$/, '');
+  // See if this string needs to be searched for anyway. The pattern ../ is
+  // stripped since it may be misinterpreted by the browser.
+  searchString = searchString.replace(/^\s+|\.{2,}\/|\s+$/g, '');
+  // Skip empty search strings, or search strings ending with a comma, since
+  // that is the separator between search terms.
   if (searchString.length <= 0 ||
   if (searchString.length <= 0 ||
     searchString.charAt(searchString.length - 1) == ',') {
     searchString.charAt(searchString.length - 1) == ',') {
     return;
     return;
@@ -306,7 +310,7 @@ Drupal.ACDB.prototype.search = function (searchString) {
         }
         }
       },
       },
       error: function (xmlhttp) {
       error: function (xmlhttp) {
-        alert(Drupal.ajaxError(xmlhttp, db.uri));
+        Drupal.displayAjaxError(Drupal.ajaxError(xmlhttp, db.uri));
       }
       }
     });
     });
   }, this.delay);
   }, this.delay);

+ 94 - 2
misc/drupal.js

@@ -269,6 +269,72 @@ Drupal.formatPlural = function (count, singular, plural, args, options) {
   }
   }
 };
 };
 
 
+/**
+ * Returns the passed in URL as an absolute URL.
+ *
+ * @param url
+ *   The URL string to be normalized to an absolute URL.
+ *
+ * @return
+ *   The normalized, absolute URL.
+ *
+ * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js
+ * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript
+ * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53
+ */
+Drupal.absoluteUrl = function (url) {
+  var urlParsingNode = document.createElement('a');
+
+  // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8
+  // strings may throw an exception.
+  try {
+    url = decodeURIComponent(url);
+  } catch (e) {}
+
+  urlParsingNode.setAttribute('href', url);
+
+  // IE <= 7 normalizes the URL when assigned to the anchor node similar to
+  // the other browsers.
+  return urlParsingNode.cloneNode(false).href;
+};
+
+/**
+ * Returns true if the URL is within Drupal's base path.
+ *
+ * @param url
+ *   The URL string to be tested.
+ *
+ * @return
+ *   Boolean true if local.
+ *
+ * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58
+ */
+Drupal.urlIsLocal = function (url) {
+  // Always use browser-derived absolute URLs in the comparison, to avoid
+  // attempts to break out of the base path using directory traversal.
+  var absoluteUrl = Drupal.absoluteUrl(url);
+  var protocol = location.protocol;
+
+  // Consider URLs that match this site's base URL but use HTTPS instead of HTTP
+  // as local as well.
+  if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) {
+    protocol = 'https:';
+  }
+  var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1);
+
+  // Decoding non-UTF-8 strings may throw an exception.
+  try {
+    absoluteUrl = decodeURIComponent(absoluteUrl);
+  } catch (e) {}
+  try {
+    baseUrl = decodeURIComponent(baseUrl);
+  } catch (e) {}
+
+  // The given URL matches the site's base URL, or has a path under the site's
+  // base URL.
+  return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0;
+};
+
 /**
 /**
  * Generate the themed representation of a Drupal object.
  * Generate the themed representation of a Drupal object.
  *
  *
@@ -347,10 +413,33 @@ Drupal.getSelection = function (element) {
   return { 'start': element.selectionStart, 'end': element.selectionEnd };
   return { 'start': element.selectionStart, 'end': element.selectionEnd };
 };
 };
 
 
+/**
+ * Add a global variable which determines if the window is being unloaded.
+ *
+ * This is primarily used by Drupal.displayAjaxError().
+ */
+Drupal.beforeUnloadCalled = false;
+$(window).bind('beforeunload pagehide', function () {
+    Drupal.beforeUnloadCalled = true;
+});
+
+/**
+ * Displays a JavaScript error from an Ajax response when appropriate to do so.
+ */
+Drupal.displayAjaxError = function (message) {
+  // Skip displaying the message if the user deliberately aborted (for example,
+  // by reloading the page or navigating to a different page) while the Ajax
+  // request was still ongoing. See, for example, the discussion at
+  // http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-clicks-refresh.
+  if (!Drupal.beforeUnloadCalled) {
+    alert(message);
+  }
+};
+
 /**
 /**
  * Build an error message from an Ajax response.
  * Build an error message from an Ajax response.
  */
  */
-Drupal.ajaxError = function (xmlhttp, uri) {
+Drupal.ajaxError = function (xmlhttp, uri, customMessage) {
   var statusCode, statusText, pathText, responseText, readyStateText, message;
   var statusCode, statusText, pathText, responseText, readyStateText, message;
   if (xmlhttp.status) {
   if (xmlhttp.status) {
     statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") +  "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
     statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") +  "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
@@ -383,7 +472,10 @@ Drupal.ajaxError = function (xmlhttp, uri) {
   // We don't need readyState except for status == 0.
   // We don't need readyState except for status == 0.
   readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
   readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
 
 
-  message = statusCode + pathText + statusText + responseText + readyStateText;
+  // Additional message beyond what the xmlhttp object provides.
+  customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : "";
+
+  message = statusCode + pathText + statusText + customMessage + responseText + readyStateText;
   return message;
   return message;
 };
 };
 
 

BIN
misc/favicon.ico


+ 5 - 1
misc/states.js

@@ -493,7 +493,11 @@ $(document).bind('state:disabled', function(e) {
 $(document).bind('state:required', function(e) {
 $(document).bind('state:required', function(e) {
   if (e.trigger) {
   if (e.trigger) {
     if (e.value) {
     if (e.value) {
-      $(e.target).closest('.form-item, .form-wrapper').find('label').append('<span class="form-required">*</span>');
+      var $label = $(e.target).closest('.form-item, .form-wrapper').find('label');
+      // Avoids duplicate required markers on initialization.
+      if (!$label.find('.form-required').length) {
+        $label.append('<span class="form-required">*</span>');
+      }
     }
     }
     else {
     else {
       $(e.target).closest('.form-item, .form-wrapper').find('label .form-required').remove();
       $(e.target).closest('.form-item, .form-wrapper').find('label .form-required').remove();

+ 19 - 6
misc/tabledrag.js

@@ -106,8 +106,10 @@ Drupal.tableDrag = function (table, tableSettings) {
 
 
   // Add mouse bindings to the document. The self variable is passed along
   // Add mouse bindings to the document. The self variable is passed along
   // as event handlers do not have direct access to the tableDrag object.
   // as event handlers do not have direct access to the tableDrag object.
-  $(document).bind('mousemove', function (event) { return self.dragRow(event, self); });
-  $(document).bind('mouseup', function (event) { return self.dropRow(event, self); });
+  $(document).bind('mousemove pointermove', function (event) { return self.dragRow(event, self); });
+  $(document).bind('mouseup pointerup', function (event) { return self.dropRow(event, self); });
+  $(document).bind('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
+  $(document).bind('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
 };
 };
 
 
 /**
 /**
@@ -274,7 +276,10 @@ Drupal.tableDrag.prototype.makeDraggable = function (item) {
   });
   });
 
 
   // Add the mousedown action for the handle.
   // Add the mousedown action for the handle.
-  handle.mousedown(function (event) {
+  handle.bind('mousedown touchstart pointerdown', function (event) {
+    if (event.originalEvent.type == "touchstart") {
+      event = event.originalEvent.touches[0];
+    }
     // Create a new dragObject recording the event information.
     // Create a new dragObject recording the event information.
     self.dragObject = {};
     self.dragObject = {};
     self.dragObject.initMouseOffset = self.getMouseOffset(item, event);
     self.dragObject.initMouseOffset = self.getMouseOffset(item, event);
@@ -500,7 +505,7 @@ Drupal.tableDrag.prototype.dragRow = function (event, self) {
     if (self.indentEnabled) {
     if (self.indentEnabled) {
       var xDiff = self.currentMouseCoords.x - self.dragObject.indentMousePos.x;
       var xDiff = self.currentMouseCoords.x - self.dragObject.indentMousePos.x;
       // Set the number of indentations the mouse has been moved left or right.
       // Set the number of indentations the mouse has been moved left or right.
-      var indentDiff = Math.round(xDiff / self.indentAmount * self.rtl);
+      var indentDiff = Math.round(xDiff / self.indentAmount);
       // Indent the row with our estimated diff, which may be further
       // Indent the row with our estimated diff, which may be further
       // restricted according to the rows around this row.
       // restricted according to the rows around this row.
       var indentChange = self.rowObject.indent(indentDiff);
       var indentChange = self.rowObject.indent(indentDiff);
@@ -575,12 +580,20 @@ Drupal.tableDrag.prototype.dropRow = function (event, self) {
  * Get the mouse coordinates from the event (allowing for browser differences).
  * Get the mouse coordinates from the event (allowing for browser differences).
  */
  */
 Drupal.tableDrag.prototype.mouseCoords = function (event) {
 Drupal.tableDrag.prototype.mouseCoords = function (event) {
+  // Complete support for pointer events was only introduced to jQuery in
+  // version 1.11.1; between versions 1.7 and 1.11.0 pointer events have the
+  // clientX and clientY properties undefined. In those cases, the properties
+  // must be retrieved from the event.originalEvent object instead.
+  var clientX = event.clientX || event.originalEvent.clientX;
+  var clientY = event.clientY || event.originalEvent.clientY;
+
   if (event.pageX || event.pageY) {
   if (event.pageX || event.pageY) {
     return { x: event.pageX, y: event.pageY };
     return { x: event.pageX, y: event.pageY };
   }
   }
+
   return {
   return {
-    x: event.clientX + document.body.scrollLeft - document.body.clientLeft,
-    y: event.clientY + document.body.scrollTop  - document.body.clientTop
+    x: clientX + document.body.scrollLeft - document.body.clientLeft,
+    y: clientY + document.body.scrollTop  - document.body.clientTop
   };
   };
 };
 };
 
 

+ 5 - 1
misc/tableselect.js

@@ -57,10 +57,14 @@ Drupal.tableSelect = function () {
     // Keep track of the last checked checkbox.
     // Keep track of the last checked checkbox.
     lastChecked = e.target;
     lastChecked = e.target;
   });
   });
+
+  // If all checkboxes are checked on page load, make sure the select-all one
+  // is checked too, otherwise keep unchecked.
+  updateSelectAll((checkboxes.length == $(checkboxes).filter(':checked').length));
 };
 };
 
 
 Drupal.tableSelectRange = function (from, to, state) {
 Drupal.tableSelectRange = function (from, to, state) {
-  // We determine the looping mode based on the the order of from and to.
+  // We determine the looping mode based on the order of from and to.
   var mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';
   var mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';
 
 
   // Traverse through the sibling nodes.
   // Traverse through the sibling nodes.

+ 6 - 0
misc/vertical-tabs.js

@@ -134,6 +134,8 @@ Drupal.verticalTab.prototype = {
   tabShow: function () {
   tabShow: function () {
     // Display the tab.
     // Display the tab.
     this.item.show();
     this.item.show();
+    // Show the vertical tabs.
+    this.item.closest('.vertical-tabs').show();
     // Update .first marker for items. We need recurse from parent to retain the
     // Update .first marker for items. We need recurse from parent to retain the
     // actual DOM element order as jQuery implements sortOrder, but not as public
     // actual DOM element order as jQuery implements sortOrder, but not as public
     // method.
     // method.
@@ -164,6 +166,10 @@ Drupal.verticalTab.prototype = {
     if ($firstTab.length) {
     if ($firstTab.length) {
       $firstTab.data('verticalTab').focus();
       $firstTab.data('verticalTab').focus();
     }
     }
+    // Hide the vertical tabs (if no tabs remain).
+    else {
+      this.item.closest('.vertical-tabs').hide();
+    }
     return this;
     return this;
   }
   }
 };
 };

+ 3 - 3
modules/aggregator/aggregator.info

@@ -7,8 +7,8 @@ files[] = aggregator.test
 configure = admin/config/services/aggregator/settings
 configure = admin/config/services/aggregator/settings
 stylesheets[all][] = aggregator.css
 stylesheets[all][] = aggregator.css
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 1 - 1
modules/aggregator/aggregator.processor.inc

@@ -72,7 +72,7 @@ function aggregator_aggregator_remove($feed) {
  */
  */
 function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
 function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
   if (in_array('aggregator', variable_get('aggregator_processors', array('aggregator')))) {
   if (in_array('aggregator', variable_get('aggregator_processors', array('aggregator')))) {
-    $info = module_invoke('aggregator', 'aggregator_process', 'info');
+    $info = module_invoke('aggregator', 'aggregator_process_info');
     $items = drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items');
     $items = drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items');
     $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
     $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
     $period[AGGREGATOR_CLEAR_NEVER] = t('Never');
     $period[AGGREGATOR_CLEAR_NEVER] = t('Never');

+ 3 - 3
modules/aggregator/tests/aggregator_test.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 25 - 0
modules/block/block.api.php

@@ -363,6 +363,31 @@ function hook_block_list_alter(&$blocks) {
   }
   }
 }
 }
 
 
+/**
+ * Act on block cache ID (cid) parts before the cid is generated.
+ *
+ * This hook allows you to add, remove or modify the custom keys used to
+ * generate a block cache ID (by default, these keys are set to the block
+ * module and delta). These keys will be combined with the standard ones
+ * provided by drupal_render_cid_parts() to generate the final block cache ID.
+ *
+ * To change the cache granularity used by drupal_render_cid_parts(), this hook
+ * cannot be used; instead, set the 'cache' key in the block's definition in
+ * hook_block_info().
+ *
+ * @params $cid_parts
+ *   An array of elements used to build the cid.
+ * @param $block
+ *   The block object being acted on.
+ *
+ * @see _block_get_cache_id()
+ */
+function hook_block_cid_parts_alter(&$cid_parts, $block) {
+  global $user;
+  // This example shows how to cache a block based on the user's timezone.
+  $cid_parts[] = $user->timezone;
+}
+
 /**
 /**
  * @} End of "addtogroup hooks".
  * @} End of "addtogroup hooks".
  */
  */

+ 3 - 3
modules/block/block.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = block.test
 files[] = block.test
 configure = admin/structure/block
 configure = admin/structure/block
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/block/block.js

@@ -24,7 +24,7 @@ Drupal.behaviors.blockSettingsSummary = {
     $('fieldset#edit-node-type', context).drupalSetSummary(function (context) {
     $('fieldset#edit-node-type', context).drupalSetSummary(function (context) {
       var vals = [];
       var vals = [];
       $('input[type="checkbox"]:checked', context).each(function () {
       $('input[type="checkbox"]:checked', context).each(function () {
-        vals.push($.trim($(this).next('label').text()));
+        vals.push($.trim($(this).next('label').html()));
       });
       });
       if (!vals.length) {
       if (!vals.length) {
         vals.push(Drupal.t('Not restricted'));
         vals.push(Drupal.t('Not restricted'));
@@ -35,7 +35,7 @@ Drupal.behaviors.blockSettingsSummary = {
     $('fieldset#edit-role', context).drupalSetSummary(function (context) {
     $('fieldset#edit-role', context).drupalSetSummary(function (context) {
       var vals = [];
       var vals = [];
       $('input[type="checkbox"]:checked', context).each(function () {
       $('input[type="checkbox"]:checked', context).each(function () {
-        vals.push($.trim($(this).next('label').text()));
+        vals.push($.trim($(this).next('label').html()));
       });
       });
       if (!vals.length) {
       if (!vals.length) {
         vals.push(Drupal.t('Not restricted'));
         vals.push(Drupal.t('Not restricted'));
@@ -49,7 +49,7 @@ Drupal.behaviors.blockSettingsSummary = {
         return Drupal.t('Not customizable');
         return Drupal.t('Not customizable');
       }
       }
       else {
       else {
-        return $radio.next('label').text();
+        return $radio.next('label').html();
       }
       }
     });
     });
   }
   }

File diff suppressed because it is too large
+ 2 - 1
modules/block/block.module


+ 2 - 2
modules/block/block.test

@@ -75,7 +75,7 @@ class BlockTestCase extends DrupalWebTestCase {
     $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
     $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
 
 
     // Check to see if the custom block was created by checking that it's in the database.
     // Check to see if the custom block was created by checking that it's in the database.
-    $this->assertNotNull($bid, 'Custom block found in database');
+    $this->assertTrue($bid, 'Custom block found in database');
 
 
     // Check that block_block_view() returns the correct title and content.
     // Check that block_block_view() returns the correct title and content.
     $data = block_block_view($bid);
     $data = block_block_view($bid);
@@ -305,7 +305,7 @@ class BlockTestCase extends DrupalWebTestCase {
     ))->fetchField();
     ))->fetchField();
 
 
     // Check to see if the block was created by checking that it's in the database.
     // Check to see if the block was created by checking that it's in the database.
-    $this->assertNotNull($bid, 'Block found in database');
+    $this->assertTrue($bid, 'Block found in database');
 
 
     // Check whether the block can be moved to all available regions.
     // Check whether the block can be moved to all available regions.
     foreach ($this->regions as $region) {
     foreach ($this->regions as $region) {

+ 3 - 3
modules/block/tests/block_test.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/block/tests/themes/block_test_theme/block_test_theme.info

@@ -13,8 +13,8 @@ regions[footer] = Footer
 regions[highlighted] = Highlighted
 regions[highlighted] = Highlighted
 regions[help] = Help
 regions[help] = Help
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/blog/blog.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = blog.test
 files[] = blog.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 1 - 1
modules/blog/blog.module

@@ -152,7 +152,7 @@ function blog_menu_local_tasks_alter(&$data, $router_item, $root_path) {
     }
     }
   }
   }
   // Provide a helper action link to the author on the 'blog/%' page.
   // Provide a helper action link to the author on the 'blog/%' page.
-  elseif ($root_path == 'blog/%' && $router_item['page_arguments'][0]->uid == $user->uid) {
+  elseif ($root_path == 'blog/%' && isset($router_item['page_arguments'][0]->uid) && $router_item['page_arguments'][0]->uid == $user->uid) {
     $data['actions']['output']['blog'] = array(
     $data['actions']['output']['blog'] = array(
       '#theme' => 'menu_local_action',
       '#theme' => 'menu_local_action',
     );
     );

+ 3 - 3
modules/book/book.info

@@ -7,8 +7,8 @@ files[] = book.test
 configure = admin/content/book/settings
 configure = admin/content/book/settings
 stylesheets[all][] = book.css
 stylesheets[all][] = book.css
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/color/color.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = color.test
 files[] = color.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/comment/comment.info

@@ -9,8 +9,8 @@ files[] = comment.test
 configure = admin/content/comment
 configure = admin/content/comment
 stylesheets[all][] = comment.css
 stylesheets[all][] = comment.css
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 5 - 2
modules/comment/comment.module

@@ -2607,7 +2607,7 @@ function comment_unpublish_action($comment, $context = array()) {
 /**
 /**
  * Unpublishes a comment if it contains certain keywords.
  * Unpublishes a comment if it contains certain keywords.
  *
  *
- * @param $comment
+ * @param object $comment
  *   Comment object to modify.
  *   Comment object to modify.
  * @param array $context
  * @param array $context
  *   Array with components:
  *   Array with components:
@@ -2619,10 +2619,13 @@ function comment_unpublish_action($comment, $context = array()) {
  * @see comment_unpublish_by_keyword_action_submit()
  * @see comment_unpublish_by_keyword_action_submit()
  */
  */
 function comment_unpublish_by_keyword_action($comment, $context) {
 function comment_unpublish_by_keyword_action($comment, $context) {
+  $node = node_load($comment->nid);
+  $build = comment_view($comment, $node);
+  $text = drupal_render($build);
   foreach ($context['keywords'] as $keyword) {
   foreach ($context['keywords'] as $keyword) {
-    $text = drupal_render($comment);
     if (strpos($text, $keyword) !== FALSE) {
     if (strpos($text, $keyword) !== FALSE) {
       $comment->status = COMMENT_NOT_PUBLISHED;
       $comment->status = COMMENT_NOT_PUBLISHED;
+      comment_save($comment);
       watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
       watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
       break;
       break;
     }
     }

+ 36 - 1
modules/comment/comment.test

@@ -13,7 +13,7 @@ class CommentHelperCase extends DrupalWebTestCase {
   function setUp() {
   function setUp() {
     parent::setUp('comment', 'search');
     parent::setUp('comment', 'search');
     // Create users and test node.
     // Create users and test node.
-    $this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks'));
+    $this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer blocks', 'administer actions', 'administer fields'));
     $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content', 'edit own comments'));
     $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content', 'edit own comments'));
     $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->uid));
     $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->uid));
   }
   }
@@ -1973,6 +1973,41 @@ class CommentActionsTestCase extends CommentHelperCase {
     $this->clearWatchdog();
     $this->clearWatchdog();
   }
   }
 
 
+  /**
+   * Tests the unpublish comment by keyword action.
+   */
+  public function testCommentUnpublishByKeyword() {
+    $this->drupalLogin($this->admin_user);
+    $callback = 'comment_unpublish_by_keyword_action';
+    $hash = drupal_hash_base64($callback);
+    $comment_text = $keywords = $this->randomName();
+    $edit = array(
+      'actions_label' => $callback,
+      'keywords' => $keywords,
+    );
+
+    $this->drupalPost("admin/config/system/actions/configure/$hash", $edit, t('Save'));
+
+    $action = db_query("SELECT aid, type, callback, parameters, label FROM {actions} WHERE callback = :callback", array(':callback' => $callback))->fetchObject();
+
+    $this->assertTrue($action, 'The action could be loaded.');
+
+    $comment = $this->postComment($this->node, $comment_text, $this->randomName());
+
+    // Load the full comment so that status is available.
+    $comment = comment_load($comment->id);
+
+    $this->assertTrue($comment->status == COMMENT_PUBLISHED, 'The comment status was set to published.');
+
+    comment_unpublish_by_keyword_action($comment, array('keywords' => array($keywords)));
+
+    // We need to make sure that the comment has been saved with status
+    // unpublished.
+    $this->assertEqual(comment_load($comment->cid)->status, COMMENT_NOT_PUBLISHED, 'Comment was unpublished.');
+    $this->assertWatchdogMessage('Unpublished comment %subject.', array('%subject' => $comment->subject), 'Found watchdog message.');
+    $this->clearWatchdog();
+  }
+
   /**
   /**
    * Verify that a watchdog message has been entered.
    * Verify that a watchdog message has been entered.
    *
    *

+ 3 - 3
modules/contact/contact.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = contact.test
 files[] = contact.test
 configure = admin/structure/contact
 configure = admin/structure/contact
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 2 - 2
modules/contact/contact.pages.inc

@@ -134,7 +134,7 @@ function contact_site_form_submit($form, &$form_state) {
   global $user, $language;
   global $user, $language;
 
 
   $values = $form_state['values'];
   $values = $form_state['values'];
-  $values['sender'] = $user;
+  $values['sender'] = clone $user;
   $values['sender']->name = $values['name'];
   $values['sender']->name = $values['name'];
   $values['sender']->mail = $values['mail'];
   $values['sender']->mail = $values['mail'];
   $values['category'] = contact_load($values['cid']);
   $values['category'] = contact_load($values['cid']);
@@ -270,7 +270,7 @@ function contact_personal_form_submit($form, &$form_state) {
   global $user, $language;
   global $user, $language;
 
 
   $values = $form_state['values'];
   $values = $form_state['values'];
-  $values['sender'] = $user;
+  $values['sender'] = clone $user;
   $values['sender']->name = $values['name'];
   $values['sender']->name = $values['name'];
   $values['sender']->mail = $values['mail'];
   $values['sender']->mail = $values['mail'];
 
 

+ 3 - 3
modules/contextual/contextual.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = contextual.test
 files[] = contextual.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/dashboard/dashboard.info

@@ -7,8 +7,8 @@ files[] = dashboard.test
 dependencies[] = block
 dependencies[] = block
 configure = admin/dashboard/customize
 configure = admin/dashboard/customize
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 8 - 1
modules/dblog/dblog.admin.inc

@@ -294,11 +294,18 @@ function theme_dblog_message($variables) {
     else {
     else {
       $output = t($event->message, unserialize($event->variables));
       $output = t($event->message, unserialize($event->variables));
     }
     }
+    // If the output is expected to be a link, strip all the tags and
+    // special characters by using filter_xss() without any allowed tags.
+    // If not, use filter_xss_admin() to allow some tags.
     if ($variables['link'] && isset($event->wid)) {
     if ($variables['link'] && isset($event->wid)) {
-      // Truncate message to 56 chars.
+      // Truncate message to 56 chars after stripping all the tags.
       $output = truncate_utf8(filter_xss($output, array()), 56, TRUE, TRUE);
       $output = truncate_utf8(filter_xss($output, array()), 56, TRUE, TRUE);
       $output = l($output, 'admin/reports/event/' . $event->wid, array('html' => TRUE));
       $output = l($output, 'admin/reports/event/' . $event->wid, array('html' => TRUE));
     }
     }
+    else {
+      // Prevent XSS in log detail pages.
+      $output = filter_xss_admin($output);
+    }
   }
   }
   return $output;
   return $output;
 }
 }

+ 3 - 3
modules/dblog/dblog.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = dblog.test
 files[] = dblog.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 9 - 0
modules/dblog/dblog.install

@@ -154,6 +154,15 @@ function dblog_update_7002() {
   db_add_index('watchdog', 'severity', array('severity'));
   db_add_index('watchdog', 'severity', array('severity'));
 }
 }
 
 
+/**
+ * Account for possible legacy systems where dblog was not installed.
+ */
+function dblog_update_7003() {
+  if (!db_table_exists('watchdog')) {
+    db_create_table('watchdog', drupal_get_schema_unprocessed('dblog', 'watchdog'));
+  }
+}
+
 /**
 /**
  * @} End of "addtogroup updates-7.x-extra".
  * @} End of "addtogroup updates-7.x-extra".
  */
  */

+ 24 - 14
modules/dblog/dblog.module

@@ -144,20 +144,30 @@ function _dblog_get_message_types() {
  * Note: Some values may be truncated to meet database column size restrictions.
  * Note: Some values may be truncated to meet database column size restrictions.
  */
  */
 function dblog_watchdog(array $log_entry) {
 function dblog_watchdog(array $log_entry) {
-  Database::getConnection('default', 'default')->insert('watchdog')
-    ->fields(array(
-      'uid' => $log_entry['uid'],
-      'type' => substr($log_entry['type'], 0, 64),
-      'message' => $log_entry['message'],
-      'variables' => serialize($log_entry['variables']),
-      'severity' => $log_entry['severity'],
-      'link' => substr($log_entry['link'], 0, 255),
-      'location' => $log_entry['request_uri'],
-      'referer' => $log_entry['referer'],
-      'hostname' => substr($log_entry['ip'], 0, 128),
-      'timestamp' => $log_entry['timestamp'],
-    ))
-    ->execute();
+  if (!function_exists('drupal_substr')) {
+    require_once DRUPAL_ROOT . '/includes/unicode.inc';
+  }
+  try {
+    Database::getConnection('default', 'default')->insert('watchdog')
+      ->fields(array(
+        'uid' => $log_entry['uid'],
+        'type' => drupal_substr($log_entry['type'], 0, 64),
+        'message' => $log_entry['message'],
+        'variables' => serialize($log_entry['variables']),
+        'severity' => $log_entry['severity'],
+        'link' => drupal_substr($log_entry['link'], 0, 255),
+        'location' => $log_entry['request_uri'],
+        'referer' => $log_entry['referer'],
+        'hostname' => drupal_substr($log_entry['ip'], 0, 128),
+        'timestamp' => $log_entry['timestamp'],
+      ))
+      ->execute();
+  }
+  catch (Exception $e) {
+    // Exception is ignored so that watchdog does not break pages during the
+    // installation process or is not able to create the watchdog table during
+    // installation.
+  }
 }
 }
 
 
 /**
 /**

File diff suppressed because it is too large
+ 3 - 0
modules/dblog/dblog.test


+ 1 - 1
modules/field/field.api.php

@@ -1897,7 +1897,7 @@ function hook_field_storage_write($entity_type, $entity, $op, $fields) {
       $items = (array) $entity->{$field_name}[$langcode];
       $items = (array) $entity->{$field_name}[$langcode];
       $delta_count = 0;
       $delta_count = 0;
       foreach ($items as $delta => $item) {
       foreach ($items as $delta => $item) {
-        // We now know we have someting to insert.
+        // We now know we have something to insert.
         $do_insert = TRUE;
         $do_insert = TRUE;
         $record = array(
         $record = array(
           'entity_type' => $entity_type,
           'entity_type' => $entity_type,

+ 3 - 3
modules/field/field.crud.inc

@@ -189,7 +189,7 @@ function field_create_field($field) {
   }
   }
 
 
   // Clear caches
   // Clear caches
-  field_cache_clear(TRUE);
+  field_cache_clear();
 
 
   // Invoke external hooks after the cache is cleared for API consistency.
   // Invoke external hooks after the cache is cleared for API consistency.
   module_invoke_all('field_create_field', $field);
   module_invoke_all('field_create_field', $field);
@@ -288,7 +288,7 @@ function field_update_field($field) {
   drupal_write_record('field_config', $field, $primary_key);
   drupal_write_record('field_config', $field, $primary_key);
 
 
   // Clear caches
   // Clear caches
-  field_cache_clear(TRUE);
+  field_cache_clear();
 
 
   // Invoke external hooks after the cache is cleared for API consistency.
   // Invoke external hooks after the cache is cleared for API consistency.
   module_invoke_all('field_update_field', $field, $prior_field, $has_data);
   module_invoke_all('field_update_field', $field, $prior_field, $has_data);
@@ -430,7 +430,7 @@ function field_delete_field($field_name) {
     ->execute();
     ->execute();
 
 
   // Clear the cache.
   // Clear the cache.
-  field_cache_clear(TRUE);
+  field_cache_clear();
 
 
   module_invoke_all('field_delete_field', $field);
   module_invoke_all('field_delete_field', $field);
 }
 }

+ 3 - 3
modules/field/field.info

@@ -11,8 +11,8 @@ dependencies[] = field_sql_storage
 required = TRUE
 required = TRUE
 stylesheets[all][] = theme/field.css
 stylesheets[all][] = theme/field.css
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 1
modules/field/field.info.class.inc

@@ -612,10 +612,12 @@ class FieldInfo {
     // Fill in default values.
     // Fill in default values.
     $display += array(
     $display += array(
       'label' => 'above',
       'label' => 'above',
-      'type' => $field_type_info['default_formatter'],
       'settings' => array(),
       'settings' => array(),
       'weight' => 0,
       'weight' => 0,
     );
     );
+    if (empty($display['type'])) {
+      $display['type'] = $field_type_info['default_formatter'];
+    }
     if ($display['type'] != 'hidden') {
     if ($display['type'] != 'hidden') {
       $formatter_type_info = field_info_formatter_types($display['type']);
       $formatter_type_info = field_info_formatter_types($display['type']);
       // Fall back to default formatter if formatter type is not available.
       // Fall back to default formatter if formatter type is not available.

+ 21 - 0
modules/field/field.install

@@ -467,6 +467,27 @@ function field_update_7003() {
   // Empty update to force a rebuild of the registry.
   // Empty update to force a rebuild of the registry.
 }
 }
 
 
+/**
+ * Grant the new "administer fields" permission to trusted users.
+ */
+function field_update_7004() {
+  // Assign the permission to anyone that already has a trusted core permission
+  // that would have previously let them administer fields on an entity type.
+  $rids = array();
+  $permissions = array(
+    'administer site configuration',
+    'administer content types',
+    'administer users',
+  );
+  foreach ($permissions as $permission) {
+    $rids = array_merge($rids, array_keys(user_roles(FALSE, $permission)));
+  }
+  $rids = array_unique($rids);
+  foreach ($rids as $rid) {
+    _update_7000_user_role_grant_permissions($rid, array('administer fields'), 'field');
+  }
+}
+
 /**
 /**
  * @} End of "addtogroup updates-7.x-extra".
  * @} End of "addtogroup updates-7.x-extra".
  */
  */

+ 16 - 0
modules/field/field.module

@@ -316,6 +316,21 @@ function field_help($path, $arg) {
   }
   }
 }
 }
 
 
+/**
+ * Implements hook_permission().
+ */
+function field_permission() {
+  return array(
+    'administer fields' => array(
+      'title' => t('Administer fields'),
+      'description' => t('Additional permissions are required based on what the fields are attached to (for example, <a href="@url">administer content types</a> to manage fields attached to content).', array(
+        '@url' => '#module-node',
+      )),
+      'restrict access' => TRUE,
+    ),
+  );
+}
+
 /**
 /**
  * Implements hook_theme().
  * Implements hook_theme().
  */
  */
@@ -894,6 +909,7 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
       'entity' => $entity,
       'entity' => $entity,
       'view_mode' => '_custom',
       'view_mode' => '_custom',
       'display' => $display,
       'display' => $display,
+      'language' => $langcode,
     );
     );
     drupal_alter('field_attach_view', $result, $context);
     drupal_alter('field_attach_view', $result, $context);
 
 

+ 3 - 3
modules/field/modules/field_sql_storage/field_sql_storage.info

@@ -7,8 +7,8 @@ dependencies[] = field
 files[] = field_sql_storage.test
 files[] = field_sql_storage.test
 required = TRUE
 required = TRUE
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 23 - 3
modules/field/modules/field_sql_storage/field_sql_storage.module

@@ -223,7 +223,17 @@ function _field_sql_storage_schema($field) {
   foreach ($field['indexes'] as $index_name => $columns) {
   foreach ($field['indexes'] as $index_name => $columns) {
     $real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
     $real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
     foreach ($columns as $column_name) {
     foreach ($columns as $column_name) {
-      $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
+      // Indexes can be specified as either a column name or an array with
+      // column name and length. Allow for either case.
+      if (is_array($column_name)) {
+        $current['indexes'][$real_name][] = array(
+          _field_sql_storage_columnname($field['field_name'], $column_name[0]),
+          $column_name[1],
+        );
+      }
+      else {
+        $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
+      }
     }
     }
   }
   }
 
 
@@ -332,7 +342,17 @@ function field_sql_storage_field_storage_update_field($field, $prior_field, $has
         $real_name = _field_sql_storage_indexname($field['field_name'], $name);
         $real_name = _field_sql_storage_indexname($field['field_name'], $name);
         $real_columns = array();
         $real_columns = array();
         foreach ($columns as $column_name) {
         foreach ($columns as $column_name) {
-          $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+          // Indexes can be specified as either a column name or an array with
+          // column name and length. Allow for either case.
+          if (is_array($column_name)) {
+            $real_columns[] = array(
+              _field_sql_storage_columnname($field['field_name'], $column_name[0]),
+              $column_name[1],
+            );
+          }
+          else {
+            $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+          }
         }
         }
         db_add_index($table, $real_name, $real_columns);
         db_add_index($table, $real_name, $real_columns);
         db_add_index($revision_table, $real_name, $real_columns);
         db_add_index($revision_table, $real_name, $real_columns);
@@ -465,7 +485,7 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel
       $items = (array) $entity->{$field_name}[$langcode];
       $items = (array) $entity->{$field_name}[$langcode];
       $delta_count = 0;
       $delta_count = 0;
       foreach ($items as $delta => $item) {
       foreach ($items as $delta => $item) {
-        // We now know we have someting to insert.
+        // We now know we have something to insert.
         $do_insert = TRUE;
         $do_insert = TRUE;
         $record = array(
         $record = array(
           'entity_type' => $entity_type,
           'entity_type' => $entity_type,

+ 2 - 2
modules/field/modules/field_sql_storage/field_sql_storage.test

@@ -355,14 +355,14 @@ class FieldSqlStorageTestCase extends DrupalWebTestCase {
     field_attach_insert('test_entity', $entity);
     field_attach_insert('test_entity', $entity);
 
 
     // Add an index
     // Add an index
-    $field = array('field_name' => $field_name, 'indexes' => array('value' => array('value')));
+    $field = array('field_name' => $field_name, 'indexes' => array('value' => array(array('value', 255))));
     field_update_field($field);
     field_update_field($field);
     foreach ($tables as $table) {
     foreach ($tables as $table) {
       $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value created in %table", array('%table' => $table)));
       $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value created in %table", array('%table' => $table)));
     }
     }
 
 
     // Add a different index, removing the existing custom one.
     // Add a different index, removing the existing custom one.
-    $field = array('field_name' => $field_name, 'indexes' => array('value_format' => array('value', 'format')));
+    $field = array('field_name' => $field_name, 'indexes' => array('value_format' => array(array('value', 127), array('format', 127))));
     field_update_field($field);
     field_update_field($field);
     foreach ($tables as $table) {
     foreach ($tables as $table) {
       $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), format_string("Index on value_format created in %table", array('%table' => $table)));
       $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), format_string("Index on value_format created in %table", array('%table' => $table)));

+ 3 - 3
modules/field/modules/list/list.info

@@ -7,8 +7,8 @@ dependencies[] = field
 dependencies[] = options
 dependencies[] = options
 files[] = tests/list.test
 files[] = tests/list.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 1 - 1
modules/field/modules/list/tests/list.test

@@ -212,7 +212,7 @@ class ListFieldUITestCase extends FieldTestCase {
     parent::setUp('field_test', 'field_ui');
     parent::setUp('field_test', 'field_ui');
 
 
     // Create test user.
     // Create test user.
-    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
     $this->drupalLogin($admin_user);
     $this->drupalLogin($admin_user);
 
 
     // Create content type, with underscores.
     // Create content type, with underscores.

+ 3 - 3
modules/field/modules/list/tests/list_test.info

@@ -5,8 +5,8 @@ package = Testing
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 3 - 3
modules/field/modules/number/number.info

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = number.test
 files[] = number.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 4 - 2
modules/field/modules/number/number.module

@@ -188,7 +188,7 @@ function number_field_formatter_info() {
       'label' => t('Default'),
       'label' => t('Default'),
       'field types' => array('number_integer'),
       'field types' => array('number_integer'),
       'settings' =>  array(
       'settings' =>  array(
-        'thousand_separator' => ' ',
+        'thousand_separator' => '',
         // The 'decimal_separator' and 'scale' settings are not configurable
         // The 'decimal_separator' and 'scale' settings are not configurable
         // through the UI, and will therefore keep their default values. They
         // through the UI, and will therefore keep their default values. They
         // are only present so that the 'number_integer' and 'number_decimal'
         // are only present so that the 'number_integer' and 'number_decimal'
@@ -202,7 +202,7 @@ function number_field_formatter_info() {
       'label' => t('Default'),
       'label' => t('Default'),
       'field types' => array('number_decimal', 'number_float'),
       'field types' => array('number_decimal', 'number_float'),
       'settings' =>  array(
       'settings' =>  array(
-        'thousand_separator' => ' ',
+        'thousand_separator' => '',
         'decimal_separator' => '.',
         'decimal_separator' => '.',
         'scale' => 2,
         'scale' => 2,
         'prefix_suffix' => TRUE,
         'prefix_suffix' => TRUE,
@@ -222,6 +222,8 @@ function number_field_formatter_settings_form($field, $instance, $view_mode, $fo
   $display = $instance['display'][$view_mode];
   $display = $instance['display'][$view_mode];
   $settings = $display['settings'];
   $settings = $display['settings'];
 
 
+  $element = array();
+
   if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
   if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
     $options = array(
     $options = array(
       ''  => t('<none>'),
       ''  => t('<none>'),

+ 1 - 1
modules/field/modules/number/number.test

@@ -23,7 +23,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
 
 
   function setUp() {
   function setUp() {
     parent::setUp('field_test');
     parent::setUp('field_test');
-    $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types'));
+    $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types', 'administer fields'));
     $this->drupalLogin($this->web_user);
     $this->drupalLogin($this->web_user);
   }
   }
 
 

+ 3 - 3
modules/field/modules/options/options.info

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = options.test
 files[] = options.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 9 - 1
modules/field/modules/options/options.module

@@ -185,6 +185,7 @@ function _options_properties($type, $multiple, $required, $has_value) {
   $base = array(
   $base = array(
     'filter_xss' => FALSE,
     'filter_xss' => FALSE,
     'strip_tags' => FALSE,
     'strip_tags' => FALSE,
+    'strip_tags_and_unescape' => FALSE,
     'empty_option' => FALSE,
     'empty_option' => FALSE,
     'optgroups' => FALSE,
     'optgroups' => FALSE,
   );
   );
@@ -195,7 +196,7 @@ function _options_properties($type, $multiple, $required, $has_value) {
     case 'select':
     case 'select':
       $properties = array(
       $properties = array(
         // Select boxes do not support any HTML tag.
         // Select boxes do not support any HTML tag.
-        'strip_tags' => TRUE,
+        'strip_tags_and_unescape' => TRUE,
         'optgroups' => TRUE,
         'optgroups' => TRUE,
       );
       );
       if ($multiple) {
       if ($multiple) {
@@ -271,9 +272,16 @@ function _options_prepare_options(&$options, $properties) {
       _options_prepare_options($options[$value], $properties);
       _options_prepare_options($options[$value], $properties);
     }
     }
     else {
     else {
+      // The 'strip_tags' option is deprecated. Use 'strip_tags_and_unescape'
+      // when plain text is required (and where the output will be run through
+      // check_plain() before being inserted back into HTML) or 'filter_xss'
+      // when HTML is required.
       if ($properties['strip_tags']) {
       if ($properties['strip_tags']) {
         $options[$value] = strip_tags($label);
         $options[$value] = strip_tags($label);
       }
       }
+      if ($properties['strip_tags_and_unescape']) {
+        $options[$value] = decode_entities(strip_tags($label));
+      }
       if ($properties['filter_xss']) {
       if ($properties['filter_xss']) {
         $options[$value] = field_filter_xss($label);
         $options[$value] = field_filter_xss($label);
       }
       }

+ 4 - 3
modules/field/modules/options/options.test

@@ -24,7 +24,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
       'cardinality' => 1,
       'cardinality' => 1,
       'settings' => array(
       'settings' => array(
         // Make sure that 0 works as an option.
         // Make sure that 0 works as an option.
-        'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+        'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',  3 => 'Some HTML encoded markup with &lt; &amp; &gt;'),
       ),
       ),
     );
     );
     $this->card_1 = field_create_field($this->card_1);
     $this->card_1 = field_create_field($this->card_1);
@@ -54,7 +54,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
     $this->bool = field_create_field($this->bool);
     $this->bool = field_create_field($this->bool);
 
 
     // Create a web user.
     // Create a web user.
-    $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+    $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer fields'));
     $this->drupalLogin($this->web_user);
     $this->drupalLogin($this->web_user);
   }
   }
 
 
@@ -233,6 +233,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
     $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
     $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
     $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
     $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
     $this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
     $this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
+    $this->assertRaw('Some HTML encoded markup with &lt; &amp; &gt;', 'HTML entities in option text were properly handled and not double-encoded');
 
 
     // Submit form: select invalid 'none' option.
     // Submit form: select invalid 'none' option.
     $edit = array("card_1[$langcode]" => '_none');
     $edit = array("card_1[$langcode]" => '_none');
@@ -459,7 +460,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
     $this->assertNoFieldChecked("edit-bool-$langcode");
     $this->assertNoFieldChecked("edit-bool-$langcode");
 
 
     // Create admin user.
     // Create admin user.
-    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
     $this->drupalLogin($admin_user);
     $this->drupalLogin($admin_user);
 
 
     // Create a test field instance.
     // Create a test field instance.

+ 3 - 3
modules/field/modules/text/text.info

@@ -7,8 +7,8 @@ dependencies[] = field
 files[] = text.test
 files[] = text.test
 required = TRUE
 required = TRUE
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 4 - 2
modules/field/modules/text/text.module

@@ -223,11 +223,13 @@ function text_field_formatter_settings_form($field, $instance, $view_mode, $form
 
 
   if (strpos($display['type'], '_trimmed') !== FALSE) {
   if (strpos($display['type'], '_trimmed') !== FALSE) {
     $element['trim_length'] = array(
     $element['trim_length'] = array(
-      '#title' => t('Trim length'),
+      '#title' => t('Trimmed limit'),
       '#type' => 'textfield',
       '#type' => 'textfield',
+      '#field_suffix' => t('characters'),
       '#size' => 10,
       '#size' => 10,
       '#default_value' => $settings['trim_length'],
       '#default_value' => $settings['trim_length'],
       '#element_validate' => array('element_validate_integer_positive'),
       '#element_validate' => array('element_validate_integer_positive'),
+      '#description' => t('If the summary is not set, the trimmed %label field will be shorter than this character limit.', array('%label' => $instance['label'])),
       '#required' => TRUE,
       '#required' => TRUE,
     );
     );
   }
   }
@@ -245,7 +247,7 @@ function text_field_formatter_settings_summary($field, $instance, $view_mode) {
   $summary = '';
   $summary = '';
 
 
   if (strpos($display['type'], '_trimmed') !== FALSE) {
   if (strpos($display['type'], '_trimmed') !== FALSE) {
-    $summary = t('Trim length') . ': ' . check_plain($settings['trim_length']);
+    $summary = t('Trimmed limit: @trim_length characters', array('@trim_length' => $settings['trim_length']));
   }
   }
 
 
   return $summary;
   return $summary;

+ 1 - 0
modules/field/modules/text/text.test

@@ -424,6 +424,7 @@ class TextTranslationTestCase extends DrupalWebTestCase {
       'administer content types',
       'administer content types',
       'access administration pages',
       'access administration pages',
       'bypass node access',
       'bypass node access',
+      'administer fields',
       filter_permission_name($full_html_format),
       filter_permission_name($full_html_format),
     ));
     ));
     $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content'));
     $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content'));

+ 2 - 1
modules/field/tests/field.test

@@ -2206,11 +2206,12 @@ class FieldDisplayAPITestCase extends FieldTestCase {
         'alter' => TRUE,
         'alter' => TRUE,
       ),
       ),
     );
     );
-    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display);
+    $output = field_view_field('test_entity', $this->entity, $this->field_name, $display, LANGUAGE_NONE);
     $this->drupalSetContent(drupal_render($output));
     $this->drupalSetContent(drupal_render($output));
     $setting = $display['settings']['test_formatter_setting_multiple'];
     $setting = $display['settings']['test_formatter_setting_multiple'];
     $this->assertNoText($this->label, 'Label was not displayed.');
     $this->assertNoText($this->label, 'Label was not displayed.');
     $this->assertText('field_test_field_attach_view_alter', 'Alter fired, display passed.');
     $this->assertText('field_test_field_attach_view_alter', 'Alter fired, display passed.');
+    $this->assertText('field language is ' . LANGUAGE_NONE, 'Language is placed onto the context.');
     $array = array();
     $array = array();
     foreach ($this->values as $delta => $value) {
     foreach ($this->values as $delta => $value) {
       $array[] = $delta . ':' . $value['value'];
       $array[] = $delta . ':' . $value['value'];

+ 3 - 3
modules/field/tests/field_test.info

@@ -6,8 +6,8 @@ files[] = field_test.entity.inc
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 4 - 0
modules/field/tests/field_test.module

@@ -220,6 +220,10 @@ function field_test_field_attach_view_alter(&$output, $context) {
   if (!empty($context['display']['settings']['alter'])) {
   if (!empty($context['display']['settings']['alter'])) {
     $output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter');
     $output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter');
   }
   }
+
+  if (isset($output['test_field'])) {
+    $output['test_field'][] = array('#markup' => 'field language is ' . $context['language']);
+  }
 }
 }
 
 
 /**
 /**

+ 4 - 0
modules/field_ui/field_ui.admin.inc

@@ -2105,6 +2105,10 @@ function field_ui_next_destination($entity_type, $bundle) {
   $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array();
   $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array();
   if (!empty($destinations)) {
   if (!empty($destinations)) {
     unset($_REQUEST['destinations']);
     unset($_REQUEST['destinations']);
+  }
+  // Remove any external URLs.
+  $destinations = array_diff($destinations, array_filter($destinations, 'url_is_external'));
+  if ($destinations) {
     return field_ui_get_destinations($destinations);
     return field_ui_get_destinations($destinations);
   }
   }
   $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
   $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);

+ 3 - 3
modules/field_ui/field_ui.info

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = field_ui.test
 files[] = field_ui.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

+ 2 - 2
modules/field_ui/field_ui.js

@@ -168,7 +168,7 @@ Drupal.fieldUIOverview = {
     var dragObject = this;
     var dragObject = this;
     var row = dragObject.rowObject.element;
     var row = dragObject.rowObject.element;
     var rowHandler = $(row).data('fieldUIRowHandler');
     var rowHandler = $(row).data('fieldUIRowHandler');
-    if (rowHandler !== undefined) {
+    if (typeof rowHandler !== 'undefined') {
       var regionRow = $(row).prevAll('tr.region-message').get(0);
       var regionRow = $(row).prevAll('tr.region-message').get(0);
       var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
       var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
 
 
@@ -319,7 +319,7 @@ Drupal.fieldUIDisplayOverview.field.prototype = {
         if (currentValue == 'hidden') {
         if (currentValue == 'hidden') {
           // Restore the formatter back to the default formatter. Pseudo-fields do
           // Restore the formatter back to the default formatter. Pseudo-fields do
           // not have default formatters, we just return to 'visible' for those.
           // not have default formatters, we just return to 'visible' for those.
-          var value = (this.defaultFormatter != undefined) ? this.defaultFormatter : 'visible';
+          var value = (typeof this.defaultFormatter !== 'undefined') ? this.defaultFormatter : this.$formatSelect.find('option').val();
         }
         }
         break;
         break;
 
 

+ 21 - 1
modules/field_ui/field_ui.module

@@ -106,9 +106,19 @@ function field_ui_menu() {
           $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
           $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
           $access += array(
           $access += array(
             'access callback' => 'user_access',
             'access callback' => 'user_access',
-            'access arguments' => array('administer site configuration'),
+            'access arguments' => array('administer fields'),
           );
           );
 
 
+          // Add the "administer fields" permission on top of the access
+          // restriction because the field UI should only be accessible to
+          // trusted users.
+          if ($access['access callback'] != 'user_access' || $access['access arguments'] != array('administer fields')) {
+            $access = array(
+              'access callback' => 'field_ui_admin_access',
+              'access arguments' => array($access['access callback'], $access['access arguments']),
+            );
+          }
+
           $items["$path/fields"] = array(
           $items["$path/fields"] = array(
             'title' => 'Manage fields',
             'title' => 'Manage fields',
             'page callback' => 'drupal_get_form',
             'page callback' => 'drupal_get_form',
@@ -392,3 +402,13 @@ function field_ui_form_node_type_form_submit($form, &$form_state) {
     $form_state['redirect'] = _field_ui_bundle_admin_path('node', $form_state['values']['type']) .'/fields';
     $form_state['redirect'] = _field_ui_bundle_admin_path('node', $form_state['values']['type']) .'/fields';
   }
   }
 }
 }
+
+/**
+ * Access callback to determine if a user is allowed to use the field UI.
+ *
+ * Only grant access if the user has both the "administer fields" permission and
+ * is granted access by the entity specific restrictions.
+ */
+function field_ui_admin_access($access_callback, $access_arguments) {
+  return user_access('administer fields') && call_user_func_array($access_callback, $access_arguments);
+}

+ 15 - 2
modules/field_ui/field_ui.test

@@ -22,7 +22,7 @@ class FieldUITestCase extends DrupalWebTestCase {
     parent::setUp($modules);
     parent::setUp($modules);
 
 
     // Create test user.
     // Create test user.
-    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy', 'administer fields'));
     $this->drupalLogin($admin_user);
     $this->drupalLogin($admin_user);
 
 
     // Create content type, with underscores.
     // Create content type, with underscores.
@@ -445,6 +445,19 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase {
     $this->assertText(t('The machine-readable name is already in use. It must be unique.'));
     $this->assertText(t('The machine-readable name is already in use. It must be unique.'));
     $this->assertUrl($url, array(), 'Stayed on the same page.');
     $this->assertUrl($url, array(), 'Stayed on the same page.');
   }
   }
+
+  /**
+   * Tests that external URLs in the 'destinations' query parameter are blocked.
+   */
+  function testExternalDestinations() {
+    $path = 'admin/structure/types/manage/article/fields/field_tags/field-settings';
+    $options = array(
+      'query' => array('destinations' => array('http://example.com')),
+    );
+    $this->drupalPost($path, NULL, t('Save field settings'), $options);
+
+    $this->assertUrl('admin/structure/types/manage/article/fields', array(), 'Stayed on the same site.');
+  }
 }
 }
 
 
 /**
 /**
@@ -682,7 +695,7 @@ class FieldUIAlterTestCase extends DrupalWebTestCase {
     parent::setUp(array('field_test'));
     parent::setUp(array('field_test'));
 
 
     // Create test user.
     // Create test user.
-    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users'));
+    $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users', 'administer fields'));
     $this->drupalLogin($admin_user);
     $this->drupalLogin($admin_user);
   }
   }
 
 

+ 7 - 1
modules/file/file.field.inc

@@ -252,6 +252,12 @@ function file_field_insert($entity_type, $entity, $field, $instance, $langcode,
  * Checks for files that have been removed from the object.
  * Checks for files that have been removed from the object.
  */
  */
 function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
 function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  // Check whether the field is defined on the object.
+  if (!isset($entity->{$field['field_name']})) {
+    // We cannot check for removed files if the field is not defined.
+    return;
+  }
+
   list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
 
 
   // On new revisions, all files are considered to be a new usage and no
   // On new revisions, all files are considered to be a new usage and no
@@ -626,7 +632,7 @@ function file_field_widget_process($element, &$form_state, $form) {
   $element['#theme'] = 'file_widget';
   $element['#theme'] = 'file_widget';
 
 
   // Add the display field if enabled.
   // Add the display field if enabled.
-  if (!empty($field['settings']['display_field']) && $item['fid']) {
+  if (!empty($field['settings']['display_field'])) {
     $element['display'] = array(
     $element['display'] = array(
       '#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
       '#type' => empty($item['fid']) ? 'hidden' : 'checkbox',
       '#title' => t('Include file in display'),
       '#title' => t('Include file in display'),

+ 3 - 3
modules/file/file.info

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = tests/file.test
 files[] = tests/file.test
 
 
-; Information added by Drupal.org packaging script on 2014-11-19
-version = "7.34"
+; Information added by Drupal.org packaging script on 2016-12-07
+version = "7.53"
 project = "drupal"
 project = "drupal"
-datestamp = "1416429488"
+datestamp = "1481152423"
 
 

Some files were not shown because too many files changed in this diff