Browse Source

drupal core updated to 7.28

Bachir Soussi Chiadmi 9 years ago
parent
commit
c3011cef61
100 changed files with 1243 additions and 1884 deletions
  1. 116 0
      CHANGELOG.txt
  2. 1 1
      COPYRIGHT.txt
  3. 6 6
      README.txt
  4. 53 6
      includes/ajax.inc
  5. 81 39
      includes/bootstrap.inc
  6. 69 25
      includes/common.inc
  7. 14 11
      includes/database/database.inc
  8. 1 1
      includes/database/mysql/database.inc
  9. 2 1
      includes/database/mysql/query.inc
  10. 1 1
      includes/database/pgsql/database.inc
  11. 2 1
      includes/database/pgsql/query.inc
  12. 37 48
      includes/database/query.inc
  13. 1 1
      includes/database/select.inc
  14. 1 1
      includes/database/sqlite/database.inc
  15. 2 1
      includes/database/sqlite/query.inc
  16. 17 15
      includes/entity.inc
  17. 0 1360
      includes/entity.inc.orig
  18. 1 1
      includes/errors.inc
  19. 85 42
      includes/file.inc
  20. 3 3
      includes/filetransfer/ftp.inc
  21. 70 21
      includes/form.inc
  22. 25 1
      includes/install.core.inc
  23. 0 1
      includes/install.inc
  24. 4 1
      includes/iso.inc
  25. 2 3
      includes/language.inc
  26. 7 6
      includes/mail.inc
  27. 16 8
      includes/menu.inc
  28. 2 2
      includes/path.inc
  29. 6 3
      includes/registry.inc
  30. 4 4
      includes/session.inc
  31. 20 22
      includes/stream_wrappers.inc
  32. 7 0
      misc/ajax.js
  33. 1 1
      misc/states.js
  34. 3 3
      modules/aggregator/aggregator.info
  35. 10 0
      modules/aggregator/aggregator.install
  36. 16 1
      modules/aggregator/aggregator.test
  37. 3 3
      modules/aggregator/tests/aggregator_test.info
  38. 14 0
      modules/aggregator/tests/aggregator_test_title_entities.xml
  39. 17 13
      modules/block/block.api.php
  40. 3 3
      modules/block/block.info
  41. 23 9
      modules/block/block.module
  42. 121 1
      modules/block/block.test
  43. 3 3
      modules/block/tests/block_test.info
  44. 31 0
      modules/block/tests/block_test.module
  45. 3 3
      modules/block/tests/themes/block_test_theme/block_test_theme.info
  46. 3 3
      modules/blog/blog.info
  47. 20 20
      modules/blog/blog.test
  48. 3 3
      modules/book/book.info
  49. 3 3
      modules/color/color.info
  50. 48 1
      modules/color/color.module
  51. 3 3
      modules/comment/comment.info
  52. 2 2
      modules/comment/comment.module
  53. 16 16
      modules/comment/comment.test
  54. 3 3
      modules/contact/contact.info
  55. 3 3
      modules/contextual/contextual.info
  56. 1 1
      modules/dashboard/dashboard.api.php
  57. 3 3
      modules/dashboard/dashboard.info
  58. 19 19
      modules/dashboard/dashboard.test
  59. 3 3
      modules/dblog/dblog.info
  60. 1 1
      modules/dblog/dblog.test
  61. 60 9
      modules/field/field.api.php
  62. 6 0
      modules/field/field.attach.inc
  63. 3 3
      modules/field/field.info
  64. 2 2
      modules/field/field.module
  65. 3 3
      modules/field/modules/field_sql_storage/field_sql_storage.info
  66. 3 3
      modules/field/modules/list/list.info
  67. 3 3
      modules/field/modules/list/tests/list_test.info
  68. 3 3
      modules/field/modules/number/number.info
  69. 3 3
      modules/field/modules/options/options.info
  70. 1 1
      modules/field/modules/options/options.test
  71. 3 3
      modules/field/modules/text/text.info
  72. 3 3
      modules/field/tests/field_test.info
  73. 2 2
      modules/field/tests/field_test.install
  74. 2 2
      modules/field_ui/field_ui.admin.inc
  75. 3 3
      modules/field_ui/field_ui.info
  76. 1 1
      modules/field_ui/field_ui.module
  77. 12 4
      modules/file/file.field.inc
  78. 3 3
      modules/file/file.info
  79. 1 1
      modules/file/file.js
  80. 1 2
      modules/file/file.module
  81. 3 3
      modules/file/tests/file_module_test.info
  82. 3 3
      modules/filter/filter.info
  83. 2 2
      modules/filter/filter.module
  84. 3 3
      modules/forum/forum.info
  85. 3 3
      modules/help/help.info
  86. 8 8
      modules/image/image.admin.inc
  87. 1 1
      modules/image/image.field.inc
  88. 3 3
      modules/image/image.info
  89. 9 3
      modules/image/image.module
  90. 13 3
      modules/image/image.test
  91. 3 3
      modules/image/tests/image_module_test.info
  92. 1 3
      modules/locale/locale.admin.inc
  93. 3 3
      modules/locale/locale.info
  94. 1 1
      modules/locale/locale.test
  95. 3 3
      modules/locale/tests/locale_test.info
  96. 2 4
      modules/menu/menu.admin.inc
  97. 3 3
      modules/menu/menu.info
  98. 3 3
      modules/node/content_types.inc
  99. 2 0
      modules/node/node.admin.inc
  100. 21 15
      modules/node/node.api.php

+ 116 - 0
CHANGELOG.txt

@@ -1,4 +1,120 @@
 
+Drupal 7.28, 2014-05-08
+-----------------------
+- Fixed a regression introduced in Drupal 7.27 that caused JavaScript to break
+  on older browsers (such as Internet Explorer 8 and earlier) when Ajax was
+  used.
+- Increased the timeout used by the Update Manager module when it fetches data
+  from drupal.org (from 5 seconds to 30 seconds), to work around a problem
+  which causes incomplete information about security updates to be presented to
+  site administrators. This fix may lead to a performance slowdown on the
+  Update Manager administration pages, when installing Drupal distributions,
+  and (for sites that use the automated cron feature) on occasional page loads
+  by site visitors.
+- Fixed the behavior of the token system's "[node:summary]" token when the body
+  field does not have a manual summary.
+- Changed the behavior of db_query_temporary() so that it works on SELECT
+  queries even when they have leading comments/whitespace. A side effect of
+  this fix is that db_query_temporary() will now fail with an error if it is
+  ever used on non-SELECT queries.
+- Added a "node_admin_filter" tag to the database query used to build the list
+  of nodes on the content administration page, to make it easier to alter.
+- Made the cron queue system log any exceptions that are thrown while an item
+  in the queue is being processed, rather than stopping the entire PHP request.
+- Improved screen reader support by adding an aria-live HTML attribute to file
+  upload fields when there is an error uploading the file (minor markup
+  change).
+- Made the pager on the Tracker module listing pages show the same number of
+  items as other pagers throughout Drupal core (minor UI change).
+- Fixed a bug which caused caches not to be properly cleared when a file entity
+  was saved or deleted.
+- Added several missing countries to the default list returned by
+  country_get_list() (string change).
+- Replaced the term "weight" with "influence" in the content ranking settings
+  for search, and added help text for administrators (string change).
+- Fixed untranslatable text strings in the administrative interface for the
+  "Crop" effect provided by the Image module (minor string change).
+- Fixed a bug in the Taxonomy module update function introduced in Drupal 7.26
+  that caused memory and CPU problems on sites with very large numbers of
+  unpublished nodes.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.27, 2014-04-16
+----------------------
+- Fixed security issues (information disclosure). See SA-CORE-2014-002.
+
+Drupal 7.26, 2014-01-15
+----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001.
+
+Drupal 7.25, 2014-01-02
+-----------------------
+- Fixed a bug in node_save() which prevented the saved node from being updated
+  in hook_node_insert() and other similar hooks.
+- Added a meta tag to install.php to prevent it from being indexed by search
+  engines even when Drupal is installed in a subfolder (minor markup change).
+- Fixed a bug in the database API that caused frequent deadlock errors when
+  running merge queries on some servers.
+- Performance improvement: Prevented block rehashing from writing blocks to the
+  database on every cache clear and cron run when the blocks have not changed.
+  This fix results in an extra 'saved' key which is added and set to TRUE for
+  each block returned by _block_rehash() that actually is saved to the database
+  (data structure change).
+- Added an optional 'skip on cron' parameter to hook_cron_queue_info() to allow
+  queues to avoid being automatically processed on cron runs (API addition).
+- Fixed a bug which caused hook_block_view_MODULE_DELTA_alter() to never be
+  invoked if the block delta had a hyphen in it. To implement the hook when the
+  block delta has a hyphen, modules should now replace hyphens with underscores
+  when constructing the function name for the hook implementation.
+- Fixed a bug which caused cached pages to sometimes be sent to the browser
+  with incorrect compression. The fix adds a new 'page_compressed' key to the
+  $cache->data array returned by drupal_page_get_cache() (minor data structure
+  change).
+- Fixed broken tests on PHP 5.5.
+- Made the File and Image modules more robust when saving entities that have
+  deleted files attached. The code in file_field_presave() will now remove the
+  record of the deleted file from the entity before saving (minor data
+  structure change).
+- Standardized menu callback functions throughout Drupal core to return
+  MENU_NOT_FOUND and MENU_ACCESS_DENIED rather than printing their own "page
+  not found" or "access denied" pages (minor API change in the return value of
+  these functions under some circumstances).
+- Fixed a bug in which caches were not properly cleared when a node was deleted
+  via the administrative interface.
+- Changed the Bartik theme to render content contained in <pre>, <code> and
+  similar tags in a larger font size, so it is easier to read.
+- Fixed a bug in the Search module that caused exceptions to be thrown during
+  searches if the server was not configured to represent decimal points as a
+  period.
+- Fixed a regression in the Image module that made image_style_url() not work
+  when a relative path (rather than a complete file URI) was passed to it.
+- Added an optional feature to the Statistics module to allow node views to be
+  tracked by Ajax requests rather than during the server-side generation of the
+  page. This allows the node counter to work on sites that use external page
+  caches (string change and new administrative option:
+  https://drupal.org/node/2164069).
+- Added a link to the drupal.org documentation page for cron to the Cron
+  settings page (string change).
+- Added a 'drupal_anonymous_user_object' variable to allow the anonymous user
+  object returned by drupal_anonymous_user() to be overridden with a classed
+  object (API addition).
+- Changed the database API to allow inserts based on a SELECT * query to work
+  correctly.
+- Changed the database schema of the {file_managed} table to allow Drupal to
+  manage files larger than 4 GB.
+- Changed the File module's hook_field_load() implementation to prevent file
+  entity properties which have the same name as file or image field properties
+  from overwriting the field properties (minor API change).
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.24, 2013-11-20
+----------------------
+- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
+
 Drupal 7.23, 2013-08-07
 -----------------------
 - Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module

+ 1 - 1
COPYRIGHT.txt

@@ -1,4 +1,4 @@
-All Drupal code is Copyright 2001 - 2012 by the original authors.
+All Drupal code is Copyright 2001 - 2013 by the original authors.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by

+ 6 - 6
README.txt

@@ -71,12 +71,12 @@ profiles/your_site_profile/themes respectively to restrict their usage to only
 sites that were installed with that specific profile.
 
 More about installation profiles and distributions:
-* Read about the difference between installation profiles and distributions:
-  http://drupal.org/node/1089736
-* Download contributed installation profiles and distributions:
-  http://drupal.org/project/distributions
-* Develop your own installation profile or distribution:
-  http://drupal.org/developing/distributions
+ * Read about the difference between installation profiles and distributions:
+   http://drupal.org/node/1089736
+ * Download contributed installation profiles and distributions:
+   http://drupal.org/project/distributions
+ * Develop your own installation profile or distribution:
+   http://drupal.org/developing/distributions
 
 APPEARANCE
 ----------

+ 53 - 6
includes/ajax.inc

@@ -308,10 +308,11 @@ function ajax_render($commands = array()) {
  * pulls the form info from $_POST.
  *
  * @return
- *   An array containing the $form and $form_state. Use the list() function
- *   to break these apart:
+ *   An array containing the $form, $form_state, $form_id, $form_build_id and an
+ *   initial list of Ajax $commands. Use the list() function to break these
+ *   apart:
  *   @code
- *     list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
+ *     list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
  *   @endcode
  */
 function ajax_get_form() {
@@ -331,6 +332,17 @@ function ajax_get_form() {
     drupal_exit();
   }
 
+  // When a page level cache is enabled, the form-build id might have been
+  // replaced from within form_get_cache. If this is the case, it is also
+  // necessary to update it in the browser by issuing an appropriate Ajax
+  // command.
+  $commands = array();
+  if (isset($form['#build_id_old']) && $form['#build_id_old'] != $form['#build_id']) {
+    // If the form build ID has changed, issue an Ajax command to update it.
+    $commands[] = ajax_command_update_build_id($form);
+    $form_build_id = $form['#build_id'];
+  }
+
   // Since some of the submit handlers are run, redirects need to be disabled.
   $form_state['no_redirect'] = TRUE;
 
@@ -345,7 +357,7 @@ function ajax_get_form() {
   $form_state['input'] = $_POST;
   $form_id = $form['#form_id'];
 
-  return array($form, $form_state, $form_id, $form_build_id);
+  return array($form, $form_state, $form_id, $form_build_id, $commands);
 }
 
 /**
@@ -366,7 +378,7 @@ function ajax_get_form() {
  * @see system_menu()
  */
 function ajax_form_callback() {
-  list($form, $form_state) = ajax_get_form();
+  list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
   drupal_process_form($form['#form_id'], $form, $form_state);
 
   // We need to return the part of the form (or some other content) that needs
@@ -379,7 +391,19 @@ function ajax_form_callback() {
     $callback = $form_state['triggering_element']['#ajax']['callback'];
   }
   if (!empty($callback) && function_exists($callback)) {
-    return $callback($form, $form_state);
+    $result = $callback($form, $form_state);
+
+    if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
+      // Turn the response into a #type=ajax array if it isn't one already.
+      $result = array(
+        '#type' => 'ajax',
+        '#commands' => ajax_prepare_response($result),
+      );
+    }
+
+    $result['#commands'] = array_merge($commands, $result['#commands']);
+
+    return $result;
   }
 }
 
@@ -1210,3 +1234,26 @@ function ajax_command_restripe($selector) {
     'selector' => $selector,
   );
 }
+
+/**
+ * Creates a Drupal Ajax 'update_build_id' command.
+ *
+ * This command updates the value of a hidden form_build_id input element on a
+ * form. It requires the form passed in to have keys for both the old build ID
+ * in #build_id_old and the new build ID in #build_id.
+ *
+ * The primary use case for this Ajax command is to serve a new build ID to a
+ * form served from the cache to an anonymous user, preventing one anonymous
+ * user from accessing the form state of another anonymous users on Ajax enabled
+ * forms.
+ *
+ * @param $form
+ *   The form array representing the form whose build ID should be updated.
+ */
+function ajax_command_update_build_id($form) {
+  return array(
+    'command' => 'updateBuildId',
+    'old' => $form['#build_id_old'],
+    'new' => $form['#build_id'],
+  );
+}

+ 81 - 39
includes/bootstrap.inc

@@ -8,7 +8,7 @@
 /**
  * The current system version.
  */
-define('VERSION', '7.23');
+define('VERSION', '7.28');
 
 /**
  * Core API compatibility.
@@ -244,7 +244,7 @@ define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
 /**
  * Regular expression to match PHP function names.
  *
- * @see http://php.net/manual/en/language.functions.php
+ * @see http://php.net/manual/language.functions.php
  */
 define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
 
@@ -278,7 +278,7 @@ define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
  * error, and $var will be populated with the contents of $object['foo'], but
  * that data will be passed by value, not reference. For more information on
  * the PHP limitation, see the note in the official PHP documentation at·
- * http://php.net/manual/en/arrayaccess.offsetget.php on
+ * http://php.net/manual/arrayaccess.offsetget.php on
  * ArrayAccess::offsetGet().
  *
  * By default, the class accounts for caches where calling functions might
@@ -683,7 +683,8 @@ function drupal_environment_initialize() {
   ini_set('session.use_only_cookies', '1');
   ini_set('session.use_trans_sid', '0');
   // Don't send HTTP headers using PHP's session handler.
-  ini_set('session.cache_limiter', 'none');
+  // An empty string is used here to disable the cache limiter.
+  ini_set('session.cache_limiter', '');
   // Use httponly session cookies.
   ini_set('session.cookie_httponly', '1');
 
@@ -1278,7 +1279,7 @@ function drupal_page_header() {
  */
 function drupal_serve_page_from_cache(stdClass $cache) {
   // Negotiate whether to use compression.
-  $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+  $page_compression = !empty($cache->data['page_compressed']);
   $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
 
   // Get headers set in hook_boot(). Keys are lower-case.
@@ -1932,6 +1933,33 @@ function drupal_block_denied($ip) {
   }
 }
 
+/**
+ * Returns a URL-safe, base64 encoded string of highly randomized bytes (over the full 8-bit range).
+ *
+ * @param $byte_count
+ *   The number of random bytes to fetch and base64 encode.
+ *
+ * @return string
+ *   The base64 encoded result will have a length of up to 4 * $byte_count.
+ */
+function drupal_random_key($byte_count = 32) {
+  return drupal_base64_encode(drupal_random_bytes($byte_count));
+}
+
+/**
+ * Returns a URL-safe, base64 encoded version of the supplied string.
+ *
+ * @param $string
+ *   The string to convert to base64.
+ *
+ * @return string
+ */
+function drupal_base64_encode($string) {
+  $data = base64_encode($string);
+  // Modify the output so it's safe to use in URLs.
+  return strtr($data, array('+' => '-', '/' => '_', '=' => ''));
+}
+
 /**
  * Returns a string of highly randomized bytes (over the full 8-bit range).
  *
@@ -1945,38 +1973,34 @@ function drupal_block_denied($ip) {
  */
 function drupal_random_bytes($count)  {
   // $random_state does not use drupal_static as it stores random bytes.
-  static $random_state, $bytes, $php_compatible;
-  // Initialize on the first call. The contents of $_SERVER includes a mix of
-  // user-specific and system information that varies a little with each page.
-  if (!isset($random_state)) {
-    $random_state = print_r($_SERVER, TRUE);
-    if (function_exists('getmypid')) {
-      // Further initialize with the somewhat random PHP process ID.
-      $random_state .= getmypid();
-    }
-    $bytes = '';
-  }
-  if (strlen($bytes) < $count) {
+  static $random_state, $bytes, $has_openssl;
+
+  $missing_bytes = $count - strlen($bytes);
+
+  if ($missing_bytes > 0) {
     // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
     // locking on Windows and rendered it unusable.
-    if (!isset($php_compatible)) {
-      $php_compatible = version_compare(PHP_VERSION, '5.3.4', '>=');
+    if (!isset($has_openssl)) {
+      $has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes');
     }
-    // /dev/urandom is available on many *nix systems and is considered the
-    // best commonly available pseudo-random source.
-    if ($fh = @fopen('/dev/urandom', 'rb')) {
+
+    // openssl_random_pseudo_bytes() will find entropy in a system-dependent
+    // way.
+    if ($has_openssl) {
+      $bytes .= openssl_random_pseudo_bytes($missing_bytes);
+    }
+
+    // Else, read directly from /dev/urandom, which is available on many *nix
+    // systems and is considered cryptographically secure.
+    elseif ($fh = @fopen('/dev/urandom', 'rb')) {
       // PHP only performs buffered reads, so in reality it will always read
       // at least 4096 bytes. Thus, it costs nothing extra to read and store
       // that much so as to speed any additional invocations.
-      $bytes .= fread($fh, max(4096, $count));
+      $bytes .= fread($fh, max(4096, $missing_bytes));
       fclose($fh);
     }
-    // openssl_random_pseudo_bytes() will find entropy in a system-dependent
-    // way.
-    elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) {
-      $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes));
-    }
-    // If /dev/urandom is not available or returns no bytes, this loop will
+
+    // If we couldn't get enough entropy, this simple hash-based PRNG will
     // generate a good set of pseudo-random bytes on any system.
     // Note that it may be important that our $random_state is passed
     // through hash() prior to being rolled into $output, that the two hash()
@@ -1984,9 +2008,23 @@ function drupal_random_bytes($count)  {
     // the microtime() - is prepended rather than appended. This is to avoid
     // directly leaking $random_state via the $output stream, which could
     // allow for trivial prediction of further "random" numbers.
-    while (strlen($bytes) < $count) {
-      $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
-      $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+    if (strlen($bytes) < $count) {
+      // Initialize on the first call. The contents of $_SERVER includes a mix of
+      // user-specific and system information that varies a little with each page.
+      if (!isset($random_state)) {
+        $random_state = print_r($_SERVER, TRUE);
+        if (function_exists('getmypid')) {
+          // Further initialize with the somewhat random PHP process ID.
+          $random_state .= getmypid();
+        }
+        $bytes = '';
+      }
+
+      do {
+        $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
+        $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+      }
+      while (strlen($bytes) < $count);
     }
   }
   $output = substr($bytes, 0, $count);
@@ -1997,17 +2035,21 @@ function drupal_random_bytes($count)  {
 /**
  * Calculates a base-64 encoded, URL-safe sha-256 hmac.
  *
- * @param $data
+ * @param string $data
  *   String to be validated with the hmac.
- * @param $key
+ * @param string $key
  *   A secret string key.
  *
- * @return
+ * @return string
  *   A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
  *   any = padding characters removed.
  */
 function drupal_hmac_base64($data, $key) {
-  $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
+  // Casting $data and $key to strings here is necessary to avoid empty string
+  // results of the hash function if they are not scalar values. As this
+  // function is used in security-critical contexts like token validation it is
+  // important that it never returns an empty string.
+  $hmac = base64_encode(hash_hmac('sha256', (string) $data, (string) $key, TRUE));
   // Modify the hmac so it's safe to use in URLs.
   return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
 }
@@ -2108,7 +2150,7 @@ function drupal_array_merge_deep_array($arrays) {
  * @return Object - the user object.
  */
 function drupal_anonymous_user() {
-  $user = new stdClass();
+  $user = variable_get('drupal_anonymous_user_object', new stdClass);
   $user->uid = 0;
   $user->hostname = ip_address();
   $user->roles = array();
@@ -3253,8 +3295,8 @@ function registry_update() {
  * However, the above line of code does not work, because PHP only allows static
  * variables to be initializied by literal values, and does not allow static
  * variables to be assigned to references.
- * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
- * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references
+ * - http://php.net/manual/language.variables.scope.php#language.variables.scope.static
+ * - http://php.net/manual/language.variables.scope.php#language.variables.scope.references
  * The example below shows the syntax needed to work around both limitations.
  * For benchmarks and more information, see http://drupal.org/node/619666.
  *

+ 69 - 25
includes/common.inc

@@ -458,7 +458,7 @@ function drupal_get_query_array($query) {
   $result = array();
   if (!empty($query)) {
     foreach (explode('&', $query) as $param) {
-      $param = explode('=', $param);
+      $param = explode('=', $param, 2);
       $result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
     }
   }
@@ -929,7 +929,7 @@ function drupal_http_request($url, array $options = array()) {
 
   // If the server URL has a user then attempt to use basic authentication.
   if (isset($uri['user'])) {
-    $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
+    $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ':'));
   }
 
   // If the database prefix is being used by SimpleTest to run the tests in a copied
@@ -1134,7 +1134,7 @@ function _fix_gpc_magic(&$item) {
  * @param $key
  *   The key for the item within $_FILES.
  *
- * @see http://php.net/manual/en/features.file-upload.php#42280
+ * @see http://php.net/manual/features.file-upload.php#42280
  */
 function _fix_gpc_magic_files(&$item, $key) {
   if ($key != 'tmp_name') {
@@ -1426,7 +1426,6 @@ function filter_xss_admin($string) {
  *   valid UTF-8.
  *
  * @see drupal_validate_utf8()
- * @ingroup sanitization
  */
 function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
   // Only operate on valid UTF-8 strings. This is necessary to prevent cross
@@ -1950,7 +1949,7 @@ function format_interval($interval, $granularity = 2, $langcode = NULL) {
  *   get interpreted as date format characters.
  * @param $timezone
  *   (optional) Time zone identifier, as described at
- *   http://php.net/manual/en/timezones.php Defaults to the time zone used to
+ *   http://php.net/manual/timezones.php Defaults to the time zone used to
  *   display the page.
  * @param $langcode
  *   (optional) Language code to translate to. Defaults to the language used to
@@ -3673,17 +3672,23 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
   if ($basepath && !file_uri_scheme($file)) {
     $file = $basepath . '/' . $file;
   }
+  // Store the parent base path to restore it later.
+  $parent_base_path = $basepath;
+  // Set the current base path to process possible child imports.
   $basepath = dirname($file);
 
   // Load the CSS stylesheet. We suppress errors because themes may specify
   // stylesheets in their .info file that don't exist in the theme's path,
   // but are merely there to disable certain module CSS files.
+  $content = '';
   if ($contents = @file_get_contents($file)) {
     // Return the processed stylesheet.
-    return drupal_load_stylesheet_content($contents, $_optimize);
+    $content = drupal_load_stylesheet_content($contents, $_optimize);
   }
 
-  return '';
+  // Restore the parent base path as the file and its childen are processed.
+  $basepath = $parent_base_path;
+  return $content;
 }
 
 /**
@@ -3700,7 +3705,7 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
  */
 function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
   // Remove multiple charset declarations for standards compliance (and fixing Safari problems).
-  $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
+  $contents = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $contents);
 
   if ($optimize) {
     // Perform some safe CSS optimizations.
@@ -3719,7 +3724,7 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
     // Remove certain whitespace.
     // There are different conditions for removing leading and trailing
     // whitespace.
-    // @see http://php.net/manual/en/regexp.reference.subpatterns.php
+    // @see http://php.net/manual/regexp.reference.subpatterns.php
     $contents = preg_replace('<
       # Strip leading and trailing whitespace.
         \s*([@{};,])\s*
@@ -3833,7 +3838,14 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
  *   The cleaned class name.
  */
 function drupal_html_class($class) {
-  return drupal_clean_css_identifier(drupal_strtolower($class));
+  // The output of this function will never change, so this uses a normal
+  // static instead of drupal_static().
+  static $classes = array();
+
+  if (!isset($classes[$class])) {
+    $classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
+  }
+  return $classes[$class];
 }
 
 /**
@@ -4097,7 +4109,7 @@ function drupal_region_class($region) {
  *       else being the same, JavaScript added by a call to drupal_add_js() that
  *       happened later in the page request gets added to the page after one for
  *       which drupal_add_js() happened earlier in the page request.
- *   - defer: If set to TRUE, the defer attribute is set on the &lt;script&gt;
+ *   - defer: If set to TRUE, the defer attribute is set on the <script>
  *     tag. Defaults to FALSE.
  *   - cache: If set to FALSE, the JavaScript file is loaded anew on every page
  *     call; in other words, it is not cached. Used only when 'type' references
@@ -5042,7 +5054,7 @@ function drupal_json_output($var = NULL) {
  */
 function drupal_get_private_key() {
   if (!($key = variable_get('drupal_private_key', 0))) {
-    $key = drupal_hash_base64(drupal_random_bytes(55));
+    $key = drupal_random_key();
     variable_set('drupal_private_key', $key);
   }
   return $key;
@@ -5054,6 +5066,11 @@ function drupal_get_private_key() {
  * @param $value
  *   An additional value to base the token on.
  *
+ * The generated token is based on the session ID of the current user. Normally,
+ * anonymous users do not have a session, so the generated token will be
+ * different on every page request. To generate a token for users without a
+ * session, manually start a session prior to calling this function.
+ *
  * @return string
  *   A 43-character URL-safe token for validation, based on the user session ID,
  *   the hash salt provided from drupal_get_hash_salt(), and the
@@ -5081,7 +5098,7 @@ function drupal_get_token($value = '') {
  */
 function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
   global $user;
-  return (($skip_anonymous && $user->uid == 0) || ($token == drupal_get_token($value)));
+  return (($skip_anonymous && $user->uid == 0) || ($token === drupal_get_token($value)));
 }
 
 function _drupal_bootstrap_full() {
@@ -5114,6 +5131,10 @@ function _drupal_bootstrap_full() {
   module_load_all();
   // Make sure all stream wrappers are registered.
   file_get_stream_wrappers();
+  // Ensure mt_rand is reseeded, to prevent random values from one page load
+  // being exploited to predict random values in subsequent page loads.
+  $seed = unpack("L", drupal_random_bytes(4));
+  mt_srand($seed[1]);
 
   $test_info = &$GLOBALS['drupal_test_info'];
   if (!empty($test_info['in_child_site'])) {
@@ -5151,7 +5172,7 @@ function _drupal_bootstrap_full() {
  * client without gzip support.
  *
  * Page compression requires the PHP zlib extension
- * (http://php.net/manual/en/ref.zlib.php).
+ * (http://php.net/manual/ref.zlib.php).
  *
  * @see drupal_page_header()
  */
@@ -5159,6 +5180,10 @@ function drupal_page_set_cache() {
   global $base_root;
 
   if (drupal_page_is_cacheable()) {
+
+    // Check whether the current page might be compressed.
+    $page_compressed = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+
     $cache = (object) array(
       'cid' => $base_root . request_uri(),
       'data' => array(
@@ -5166,6 +5191,9 @@ function drupal_page_set_cache() {
         'body' => ob_get_clean(),
         'title' => drupal_get_title(),
         'headers' => array(),
+        // We need to store whether page was compressed or not,
+        // because by the time it is read, the configuration might change.
+        'page_compressed' => $page_compressed,
       ),
       'expire' => CACHE_TEMPORARY,
       'created' => REQUEST_TIME,
@@ -5183,7 +5211,7 @@ function drupal_page_set_cache() {
     }
 
     if ($cache->data['body']) {
-      if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
+      if ($page_compressed) {
         $cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP);
       }
       cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire);
@@ -5258,12 +5286,23 @@ function drupal_cron_run() {
   }
 
   foreach ($queues as $queue_name => $info) {
+    if (!empty($info['skip on cron'])) {
+      // Do not run if queue wants to skip.
+      continue;
+    }
     $function = $info['worker callback'];
     $end = time() + (isset($info['time']) ? $info['time'] : 15);
     $queue = DrupalQueue::get($queue_name);
     while (time() < $end && ($item = $queue->claimItem())) {
-      $function($item->data);
-      $queue->deleteItem($item);
+      try {
+        $function($item->data);
+        $queue->deleteItem($item);
+      }
+      catch (Exception $e) {
+        // In case of exception log it and leave the item in the queue
+        // to be processed again later.
+        watchdog_exception('cron', $e);
+      }
     }
   }
   // Restore the user.
@@ -5918,14 +5957,16 @@ function drupal_render(&$elements) {
 /**
  * Renders children of an element and concatenates them.
  *
- * This renders all children of an element using drupal_render() and then
- * joins them together into a single string.
- *
- * @param $element
+ * @param array $element
  *   The structured array whose children shall be rendered.
- * @param $children_keys
- *   If the keys of the element's children are already known, they can be passed
- *   in to save another run of element_children().
+ * @param array $children_keys
+ *   (optional) If the keys of the element's children are already known, they
+ *   can be passed in to save another run of element_children().
+ *
+ * @return string
+ *   The rendered HTML of all children of the element.
+
+ * @see drupal_render()
  */
 function drupal_render_children(&$element, $children_keys = NULL) {
   if ($children_keys === NULL) {
@@ -7799,7 +7840,10 @@ function entity_load_unchanged($entity_type, $id) {
 }
 
 /**
- * Get the entity controller class for an entity type.
+ * Gets the entity controller for an entity type.
+ *
+ * @return DrupalEntityControllerInterface
+ *   The entity controller object for the specified entity type.
  */
 function entity_get_controller($entity_type) {
   $controllers = &drupal_static(__FUNCTION__, array());

+ 14 - 11
includes/database/database.inc

@@ -28,18 +28,21 @@
  * Most Drupal database SELECT queries are performed by a call to db_query() or
  * db_query_range(). Module authors should also consider using the PagerDefault
  * Extender for queries that return results that need to be presented on
- * multiple pages, and the Tablesort Extender for generating appropriate queries
- * for sortable tables.
+ * multiple pages (see https://drupal.org/node/508796), and the TableSort
+ * Extender for generating appropriate queries for sortable tables
+ * (see https://drupal.org/node/1848372).
  *
  * For example, one might wish to return a list of the most recent 10 nodes
  * authored by a given user. Instead of directly issuing the SQL query
  * @code
- * SELECT n.nid, n.title, n.created FROM node n WHERE n.uid = $uid LIMIT 0, 10;
+ * SELECT n.nid, n.title, n.created FROM node n WHERE n.uid = $uid
+ *   ORDER BY n.created DESC LIMIT 0, 10;
  * @endcode
  * one would instead call the Drupal functions:
  * @code
  * $result = db_query_range('SELECT n.nid, n.title, n.created
- *   FROM {node} n WHERE n.uid = :uid', 0, 10, array(':uid' => $uid));
+ *   FROM {node} n WHERE n.uid = :uid
+ *   ORDER BY n.created DESC', 0, 10, array(':uid' => $uid));
  * foreach ($result as $record) {
  *   // Perform operations on $record->title, etc. here.
  * }
@@ -179,7 +182,7 @@
  * concrete implementation of it to support special handling required by that
  * database.
  *
- * @see http://php.net/manual/en/book.pdo.php
+ * @see http://php.net/manual/book.pdo.php
  */
 abstract class DatabaseConnection extends PDO {
 
@@ -1986,7 +1989,7 @@ interface DatabaseStatementInterface extends Traversable {
   /**
    * Sets the default fetch mode for this statement.
    *
-   * See http://php.net/manual/en/pdo.constants.php for the definition of the
+   * See http://php.net/manual/pdo.constants.php for the definition of the
    * constants used.
    *
    * @param $mode
@@ -2005,7 +2008,7 @@ interface DatabaseStatementInterface extends Traversable {
   /**
    * Fetches the next row from a result set.
    *
-   * See http://php.net/manual/en/pdo.constants.php for the definition of the
+   * See http://php.net/manual/pdo.constants.php for the definition of the
    * constants used.
    *
    * @param $mode
@@ -2380,14 +2383,14 @@ function db_query_range($query, $from, $count, array $args = array(), array $opt
 }
 
 /**
- * Executes a query string and saves the result set to a temporary table.
+ * Executes a SELECT query string and saves the result set to a temporary table.
  *
  * The execution of the query string happens against the active database.
  *
  * @param $query
- *   The prepared statement query to run. Although it will accept both named and
- *   unnamed placeholders, named placeholders are strongly preferred as they are
- *   more self-documenting.
+ *   The prepared SELECT statement query to run. Although it will accept both
+ *   named and unnamed placeholders, named placeholders are strongly preferred
+ *   as they are more self-documenting.
  * @param $args
  *   An array of values to substitute into the query. If the query uses named
  *   placeholders, this is an associative array in any order. If the query uses

+ 1 - 1
includes/database/mysql/database.inc

@@ -90,7 +90,7 @@ class DatabaseConnection_mysql extends DatabaseConnection {
 
   public function queryTemporary($query, array $args = array(), array $options = array()) {
     $tablename = $this->generateTemporaryTableName();
-    $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} Engine=MEMORY SELECT', $query), $args, $options);
+    $this->query('CREATE TEMPORARY TABLE {' . $tablename . '} Engine=MEMORY ' . $query, $args, $options);
     return $tablename;
   }
 

+ 2 - 1
includes/database/mysql/query.inc

@@ -51,7 +51,8 @@ class InsertQuery_mysql extends InsertQuery {
     // If we're selecting from a SelectQuery, finish building the query and
     // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
-      return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
+      $insert_fields_string = $insert_fields ? ' (' . implode(', ', $insert_fields) . ') ' : ' ';
+      return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery;
     }
 
     $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';

+ 1 - 1
includes/database/pgsql/database.inc

@@ -146,7 +146,7 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
 
   public function queryTemporary($query, array $args = array(), array $options = array()) {
     $tablename = $this->generateTemporaryTableName();
-    $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE {' . $tablename . '} AS SELECT', $query), $args, $options);
+    $this->query('CREATE TEMPORARY TABLE {' . $tablename . '} AS ' . $query, $args, $options);
     return $tablename;
   }
 

+ 2 - 1
includes/database/pgsql/query.inc

@@ -112,7 +112,8 @@ class InsertQuery_pgsql extends InsertQuery {
     // If we're selecting from a SelectQuery, finish building the query and
     // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
-      return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') ' . $this->fromQuery;
+      $insert_fields_string = $insert_fields ? ' (' . implode(', ', $insert_fields) . ') ' : ' ';
+      return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery;
     }
 
     $query = $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $insert_fields) . ') VALUES ';

+ 37 - 48
includes/database/query.inc

@@ -710,10 +710,11 @@ class InsertQuery extends Query {
       // first call to fields() does have an effect.
       $this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions())));
     }
-
-    // Don't execute query without fields.
-    if (count($this->insertFields) + count($this->defaultFields) == 0) {
-      throw new NoFieldsException('There are no fields available to insert with.');
+    else {
+      // Don't execute query without fields.
+      if (count($this->insertFields) + count($this->defaultFields) == 0) {
+        throw new NoFieldsException('There are no fields available to insert with.');
+      }
     }
 
     // If no values have been added, silently ignore this query. This can happen
@@ -1605,55 +1606,43 @@ class MergeQuery extends Query implements QueryConditionInterface {
   }
 
   public function execute() {
-    // Wrap multiple queries in a transaction, if the database supports it.
-    $transaction = $this->connection->startTransaction();
-    try {
-      if (!count($this->condition)) {
-        throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
-      }
-      $select = $this->connection->select($this->conditionTable)
-        ->condition($this->condition)
-        ->forUpdate();
-      $select->addExpression('1');
-      if (!$select->execute()->fetchField()) {
-        try {
-          $insert = $this->connection->insert($this->table)->fields($this->insertFields);
-          if ($this->defaultFields) {
-            $insert->useDefaults($this->defaultFields);
-          }
-          $insert->execute();
-          return MergeQuery::STATUS_INSERT;
-        }
-        catch (Exception $e) {
-          // The insert query failed, maybe it's because a racing insert query
-          // beat us in inserting the same row. Retry the select query, if it
-          // returns a row, ignore the error and continue with the update
-          // query below.
-          if (!$select->execute()->fetchField()) {
-            throw $e;
-          }
+    if (!count($this->condition)) {
+      throw new InvalidMergeQueryException(t('Invalid merge query: no conditions'));
+    }
+    $select = $this->connection->select($this->conditionTable)
+      ->condition($this->condition);
+    $select->addExpression('1');
+    if (!$select->execute()->fetchField()) {
+      try {
+        $insert = $this->connection->insert($this->table)->fields($this->insertFields);
+        if ($this->defaultFields) {
+          $insert->useDefaults($this->defaultFields);
         }
+        $insert->execute();
+        return self::STATUS_INSERT;
       }
-      if ($this->needsUpdate) {
-        $update = $this->connection->update($this->table)
-          ->fields($this->updateFields)
-          ->condition($this->condition);
-        if ($this->expressionFields) {
-          foreach ($this->expressionFields as $field => $data) {
-            $update->expression($field, $data['expression'], $data['arguments']);
-          }
+      catch (Exception $e) {
+        // The insert query failed, maybe it's because a racing insert query
+        // beat us in inserting the same row. Retry the select query, if it
+        // returns a row, ignore the error and continue with the update
+        // query below.
+        if (!$select->execute()->fetchField()) {
+          throw $e;
         }
-        $update->execute();
-        return MergeQuery::STATUS_UPDATE;
       }
     }
-    catch (Exception $e) {
-      // Something really wrong happened here, bubble up the exception to the
-      // caller.
-      $transaction->rollback();
-      throw $e;
-    }
-    // Transaction commits here where $transaction looses scope.
+    if ($this->needsUpdate) {
+      $update = $this->connection->update($this->table)
+        ->fields($this->updateFields)
+        ->condition($this->condition);
+      if ($this->expressionFields) {
+        foreach ($this->expressionFields as $field => $data) {
+          $update->expression($field, $data['expression'], $data['arguments']);
+        }
+      }
+      $update->execute();
+      return self::STATUS_UPDATE;
+     }
   }
 }
 

+ 1 - 1
includes/database/select.inc

@@ -596,7 +596,7 @@ class SelectQueryExtender implements SelectQueryInterface {
 
   public function hasAnyTag() {
     $args = func_get_args();
-    return call_user_func_array(array($this->query, 'hasAnyTags'), $args);
+    return call_user_func_array(array($this->query, 'hasAnyTag'), $args);
   }
 
   public function addMetaData($key, $object) {

+ 1 - 1
includes/database/sqlite/database.inc

@@ -250,7 +250,7 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
     $prefixes[$tablename] = '';
     $this->setPrefix($prefixes);
 
-    $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options);
+    $this->query('CREATE TEMPORARY TABLE ' . $tablename . ' AS ' . $query, $args, $options);
     return $tablename;
   }
 

+ 2 - 1
includes/database/sqlite/query.inc

@@ -41,7 +41,8 @@ class InsertQuery_sqlite extends InsertQuery {
     // If we're selecting from a SelectQuery, finish building the query and
     // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
-      return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') ' . $this->fromQuery;
+      $insert_fields_string = $this->insertFields ? ' (' . implode(', ', $this->insertFields) . ') ' : ' ';
+      return $comments . 'INSERT INTO {' . $this->table . '}' . $insert_fields_string . $this->fromQuery;
     }
 
     return $comments . 'INSERT INTO {' . $this->table . '} (' . implode(', ', $this->insertFields) . ') VALUES (' . implode(', ', $placeholders) . ')';

+ 17 - 15
includes/entity.inc

@@ -154,18 +154,6 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
    */
   public function load($ids = array(), $conditions = array()) {
     $entities = array();
-    
-    # PATCH http://drupal.org/node/1003788#comment-5195682
-    // Clean the $ids array to remove non-integer values that can be passed
-    // in from various sources, including menu callbacks.
-    if (is_array($ids)) {
-      foreach ($ids as $key => $id) {
-        if (empty($id) || ((string) $id !== (string) (int) $id)) {
-          unset($ids[$key]);
-        }
-      }
-    }
-    # endpatch
 
     // Revisions are not statically cached, and require a different query to
     // other conditions, so separate the revision id into its own variable.
@@ -372,9 +360,23 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
     // This ensures the same behavior whether loading from memory or database.
     if ($conditions) {
       foreach ($entities as $entity) {
-        $entity_values = (array) $entity;
-        if (array_diff_assoc($conditions, $entity_values)) {
-          unset($entities[$entity->{$this->idKey}]);
+        // Iterate over all conditions and compare them to the entity
+        // properties. We cannot use array_diff_assoc() here since the
+        // conditions can be nested arrays, too.
+        foreach ($conditions as $property_name => $condition) {
+          if (is_array($condition)) {
+            // Multiple condition values for one property are treated as OR
+            // operation: only if the value is not at all in the condition array
+            // we remove the entity.
+            if (!in_array($entity->{$property_name}, $condition)) {
+              unset($entities[$entity->{$this->idKey}]);
+              continue 2;
+            }
+          }
+          elseif ($condition != $entity->{$property_name}) {
+            unset($entities[$entity->{$this->idKey}]);
+            continue 2;
+          }
         }
       }
     }

+ 0 - 1360
includes/entity.inc.orig

@@ -1,1360 +0,0 @@
-<?php
-
-/**
- * Interface for entity controller classes.
- *
- * All entity controller classes specified via the 'controller class' key
- * returned by hook_entity_info() or hook_entity_info_alter() have to implement
- * this interface.
- *
- * Most simple, SQL-based entity controllers will do better by extending
- * DrupalDefaultEntityController instead of implementing this interface
- * directly.
- */
-interface DrupalEntityControllerInterface {
-
-  /**
-   * Resets the internal, static entity cache.
-   *
-   * @param $ids
-   *   (optional) If specified, the cache is reset for the entities with the
-   *   given ids only.
-   */
-  public function resetCache(array $ids = NULL);
-
-  /**
-   * Loads one or more entities.
-   *
-   * @param $ids
-   *   An array of entity IDs, or FALSE to load all entities.
-   * @param $conditions
-   *   An array of conditions in the form 'field' => $value.
-   *
-   * @return
-   *   An array of entity objects indexed by their ids. When no results are
-   *   found, an empty array is returned.
-   */
-  public function load($ids = array(), $conditions = array());
-}
-
-/**
- * Default implementation of DrupalEntityControllerInterface.
- *
- * This class can be used as-is by most simple entity types. Entity types
- * requiring special handling can extend the class.
- */
-class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
-
-  /**
-   * Static cache of entities.
-   *
-   * @var array
-   */
-  protected $entityCache;
-
-  /**
-   * Entity type for this controller instance.
-   *
-   * @var string
-   */
-  protected $entityType;
-
-  /**
-   * Array of information about the entity.
-   *
-   * @var array
-   *
-   * @see entity_get_info()
-   */
-  protected $entityInfo;
-
-  /**
-   * Additional arguments to pass to hook_TYPE_load().
-   *
-   * Set before calling DrupalDefaultEntityController::attachLoad().
-   *
-   * @var array
-   */
-  protected $hookLoadArguments;
-
-  /**
-   * Name of the entity's ID field in the entity database table.
-   *
-   * @var string
-   */
-  protected $idKey;
-
-  /**
-   * Name of entity's revision database table field, if it supports revisions.
-   *
-   * Has the value FALSE if this entity does not use revisions.
-   *
-   * @var string
-   */
-  protected $revisionKey;
-
-  /**
-   * The table that stores revisions, if the entity supports revisions.
-   *
-   * @var string
-   */
-  protected $revisionTable;
-
-  /**
-   * Whether this entity type should use the static cache.
-   *
-   * Set by entity info.
-   *
-   * @var boolean
-   */
-  protected $cache;
-
-  /**
-   * Constructor: sets basic variables.
-   *
-   * @param $entityType
-   *   The entity type for which the instance is created.
-   */
-  public function __construct($entityType) {
-    $this->entityType = $entityType;
-    $this->entityInfo = entity_get_info($entityType);
-    $this->entityCache = array();
-    $this->hookLoadArguments = array();
-    $this->idKey = $this->entityInfo['entity keys']['id'];
-
-    // Check if the entity type supports revisions.
-    if (!empty($this->entityInfo['entity keys']['revision'])) {
-      $this->revisionKey = $this->entityInfo['entity keys']['revision'];
-      $this->revisionTable = $this->entityInfo['revision table'];
-    }
-    else {
-      $this->revisionKey = FALSE;
-    }
-
-    // Check if the entity type supports static caching of loaded entities.
-    $this->cache = !empty($this->entityInfo['static cache']);
-  }
-
-  /**
-   * Implements DrupalEntityControllerInterface::resetCache().
-   */
-  public function resetCache(array $ids = NULL) {
-    if (isset($ids)) {
-      foreach ($ids as $id) {
-        unset($this->entityCache[$id]);
-      }
-    }
-    else {
-      $this->entityCache = array();
-    }
-  }
-
-  /**
-   * Implements DrupalEntityControllerInterface::load().
-   */
-  public function load($ids = array(), $conditions = array()) {
-    $entities = array();
-
-    // Revisions are not statically cached, and require a different query to
-    // other conditions, so separate the revision id into its own variable.
-    if ($this->revisionKey && isset($conditions[$this->revisionKey])) {
-      $revision_id = $conditions[$this->revisionKey];
-      unset($conditions[$this->revisionKey]);
-    }
-    else {
-      $revision_id = FALSE;
-    }
-
-    // Create a new variable which is either a prepared version of the $ids
-    // array for later comparison with the entity cache, or FALSE if no $ids
-    // were passed. The $ids array is reduced as items are loaded from cache,
-    // and we need to know if it's empty for this reason to avoid querying the
-    // database when all requested entities are loaded from cache.
-    $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
-    // Try to load entities from the static cache, if the entity type supports
-    // static caching.
-    if ($this->cache && !$revision_id) {
-      $entities += $this->cacheGet($ids, $conditions);
-      // If any entities were loaded, remove them from the ids still to load.
-      if ($passed_ids) {
-        $ids = array_keys(array_diff_key($passed_ids, $entities));
-      }
-    }
-
-    // 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
-    // load, if loading a revision, or if $conditions was passed without $ids.
-    if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) {
-      // Build the query.
-      $query = $this->buildQuery($ids, $conditions, $revision_id);
-      $queried_entities = $query
-        ->execute()
-        ->fetchAllAssoc($this->idKey);
-    }
-
-    // Pass all entities loaded from the database through $this->attachLoad(),
-    // which attaches fields (if supported by the entity type) and calls the
-    // entity type specific load callback, for example hook_node_load().
-    if (!empty($queried_entities)) {
-      $this->attachLoad($queried_entities, $revision_id);
-      $entities += $queried_entities;
-    }
-
-    if ($this->cache) {
-      // Add entities to the cache if we are not loading a revision.
-      if (!empty($queried_entities) && !$revision_id) {
-        $this->cacheSet($queried_entities);
-      }
-    }
-
-    // Ensure that the returned array is ordered the same as the original
-    // $ids array if this was passed in and remove any invalid ids.
-    if ($passed_ids) {
-      // Remove any invalid ids from the array.
-      $passed_ids = array_intersect_key($passed_ids, $entities);
-      foreach ($entities as $entity) {
-        $passed_ids[$entity->{$this->idKey}] = $entity;
-      }
-      $entities = $passed_ids;
-    }
-
-    return $entities;
-  }
-
-  /**
-   * Builds the query to load the entity.
-   *
-   * This has full revision support. For entities requiring special queries,
-   * the class can be extended, and the default query can be constructed by
-   * calling parent::buildQuery(). This is usually necessary when the object
-   * being loaded needs to be augmented with additional data from another
-   * table, such as loading node type into comments or vocabulary machine name
-   * into terms, however it can also support $conditions on different tables.
-   * See CommentController::buildQuery() or TaxonomyTermController::buildQuery()
-   * for examples.
-   *
-   * @param $ids
-   *   An array of entity IDs, or FALSE to load all entities.
-   * @param $conditions
-   *   An array of conditions in the form 'field' => $value.
-   * @param $revision_id
-   *   The ID of the revision to load, or FALSE if this query is asking for the
-   *   most current revision(s).
-   *
-   * @return SelectQuery
-   *   A SelectQuery object for loading the entity.
-   */
-  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
-    $query = db_select($this->entityInfo['base table'], 'base');
-
-    $query->addTag($this->entityType . '_load_multiple');
-
-    if ($revision_id) {
-      $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id));
-    }
-    elseif ($this->revisionKey) {
-      $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
-    }
-
-    // Add fields from the {entity} table.
-    $entity_fields = $this->entityInfo['schema_fields_sql']['base table'];
-
-    if ($this->revisionKey) {
-      // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']);
-      // The id field is provided by entity, so remove it.
-      unset($entity_revision_fields[$this->idKey]);
-
-      // Remove all fields from the base table that are also fields by the same
-      // name in the revision table.
-      $entity_field_keys = array_flip($entity_fields);
-      foreach ($entity_revision_fields as $key => $name) {
-        if (isset($entity_field_keys[$name])) {
-          unset($entity_fields[$entity_field_keys[$name]]);
-        }
-      }
-      $query->fields('revision', $entity_revision_fields);
-    }
-
-    $query->fields('base', $entity_fields);
-
-    if ($ids) {
-      $query->condition("base.{$this->idKey}", $ids, 'IN');
-    }
-    if ($conditions) {
-      foreach ($conditions as $field => $value) {
-        $query->condition('base.' . $field, $value);
-      }
-    }
-    return $query;
-  }
-
-  /**
-   * Attaches data to entities upon loading.
-   *
-   * This will attach fields, if the entity is fieldable. It calls
-   * hook_entity_load() for modules which need to add data to all entities.
-   * It also calls hook_TYPE_load() on the loaded entities. For example
-   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
-   * expects special parameters apart from the queried entities, you can set
-   * $this->hookLoadArguments prior to calling the method.
-   * See NodeController::attachLoad() for an example.
-   *
-   * @param $queried_entities
-   *   Associative array of query results, keyed on the entity ID.
-   * @param $revision_id
-   *   ID of the revision that was loaded, or FALSE if the most current revision
-   *   was loaded.
-   */
-  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
-    // Attach fields.
-    if ($this->entityInfo['fieldable']) {
-      if ($revision_id) {
-        field_attach_load_revision($this->entityType, $queried_entities);
-      }
-      else {
-        field_attach_load($this->entityType, $queried_entities);
-      }
-    }
-
-    // Call hook_entity_load().
-    foreach (module_implements('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (module_implements($this->entityInfo['load hook']) as $module) {
-      call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
-    }
-  }
-
-  /**
-   * Gets entities from the static cache.
-   *
-   * @param $ids
-   *   If not empty, return entities that match these IDs.
-   * @param $conditions
-   *   If set, return entities that match all of these conditions.
-   *
-   * @return
-   *   Array of entities from the entity cache.
-   */
-  protected function cacheGet($ids, $conditions = array()) {
-    $entities = array();
-    // Load any available entities from the internal cache.
-    if (!empty($this->entityCache)) {
-      if ($ids) {
-        $entities += array_intersect_key($this->entityCache, array_flip($ids));
-      }
-      // If loading entities only by conditions, fetch all available entities
-      // from the cache. Entities which don't match are removed later.
-      elseif ($conditions) {
-        $entities = $this->entityCache;
-      }
-    }
-
-    // Exclude any entities loaded from cache if they don't match $conditions.
-    // This ensures the same behavior whether loading from memory or database.
-    if ($conditions) {
-      foreach ($entities as $entity) {
-        $entity_values = (array) $entity;
-        if (array_diff_assoc($conditions, $entity_values)) {
-          unset($entities[$entity->{$this->idKey}]);
-        }
-      }
-    }
-    return $entities;
-  }
-
-  /**
-   * Stores entities in the static entity cache.
-   *
-   * @param $entities
-   *   Entities to store in the cache.
-   */
-  protected function cacheSet($entities) {
-    $this->entityCache += $entities;
-  }
-}
-
-/**
- * Exception thrown by EntityFieldQuery() on unsupported query syntax.
- *
- * Some storage modules might not support the full range of the syntax for
- * conditions, and will raise an EntityFieldQueryException when an unsupported
- * condition was specified.
- */
-class EntityFieldQueryException extends Exception {}
-
-/**
- * Retrieves entities matching a given set of conditions.
- *
- * This class allows finding entities based on entity properties (for example,
- * node->changed), field values, and generic entity meta data (bundle,
- * 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
- * nodes written by users created in the last hour, as this would require
- * querying both node->status and user->created.
- *
- * Normally we would not want to have public properties on the object, as that
- * allows the object's state to become inconsistent too easily. However, this
- * class's standard use case involves primarily code that does need to have
- * direct access to the collected properties in order to handle alternate
- * execution routines. We therefore use public properties for simplicity. Note
- * that code that is simply creating and running a field query should still use
- * the appropriate methods to add conditions on the query.
- *
- * Storage engines are not required to support every type of query. By default,
- * an EntityFieldQueryException will be raised if an unsupported condition is
- * specified or if the query has field conditions or sorts that are stored in
- * different field storage engines. However, this logic can be overridden in
- * hook_entity_query_alter().
- *
- * Also note that this query does not automatically respect entity access
- * restrictions. Node access control is performed by the SQL storage engine but
- * other storage engines might not do this.
- */
-class EntityFieldQuery {
-
-  /**
-   * Indicates that both deleted and non-deleted fields should be returned.
-   *
-   * @see EntityFieldQuery::deleted()
-   */
-  const RETURN_ALL = NULL;
-
-  /**
-   * TRUE if the query has already been altered, FALSE if it hasn't.
-   *
-   * Used in alter hooks to check for cloned queries that have already been
-   * altered prior to the clone (for example, the pager count query).
-   *
-   * @var boolean
-   */
-  public $altered = FALSE;
-
-  /**
-   * Associative array of entity-generic metadata conditions.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::entityCondition()
-   */
-  public $entityConditions = array();
-
-  /**
-   * List of field conditions.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::fieldCondition()
-   */
-  public $fieldConditions = array();
-
-  /**
-   * List of field meta conditions (language and delta).
-   *
-   * Field conditions operate on columns specified by hook_field_schema(),
-   * the meta conditions operate on columns added by the system: delta
-   * and language. These can not be mixed with the field conditions because
-   * field columns can have any name including delta and language.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::fieldLanguageCondition()
-   * @see EntityFieldQuery::fieldDeltaCondition()
-   */
-  public $fieldMetaConditions = array();
-
-  /**
-   * List of property conditions.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::propertyCondition()
-   */
-  public $propertyConditions = array();
-
-  /**
-   * List of order clauses.
-   *
-   * @var array
-   */
-  public $order = array();
-
-  /**
-   * The query range.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::range()
-   */
-  public $range = array();
-
-  /**
-   * The query pager data.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::pager()
-   */
-  public $pager = array();
-
-  /**
-   * Query behavior for deleted data.
-   *
-   * TRUE to return only deleted data, FALSE to return only non-deleted data,
-   * EntityFieldQuery::RETURN_ALL to return everything.
-   *
-   * @see EntityFieldQuery::deleted()
-   */
-  public $deleted = FALSE;
-
-  /**
-   * A list of field arrays used.
-   *
-   * Field names passed to EntityFieldQuery::fieldCondition() and
-   * EntityFieldQuery::fieldOrderBy() are run through field_info_field() before
-   * stored in this array. This way, the elements of this array are field
-   * arrays.
-   *
-   * @var array
-   */
-  public $fields = array();
-
-  /**
-   * TRUE if this is a count query, FALSE if it isn't.
-   *
-   * @var boolean
-   */
-  public $count = FALSE;
-
-  /**
-   * Flag indicating whether this is querying current or all revisions.
-   *
-   * @var int
-   *
-   * @see EntityFieldQuery::age()
-   */
-  public $age = FIELD_LOAD_CURRENT;
-
-  /**
-   * A list of the tags added to this query.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::addTag()
-   */
-  public $tags = array();
-
-  /**
-   * A list of metadata added to this query.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::addMetaData()
-   */
-  public $metaData = array();
-
-  /**
-   * The ordered results.
-   *
-   * @var array
-   *
-   * @see EntityFieldQuery::execute().
-   */
-  public $orderedResults = array();
-
-  /**
-   * The method executing the query, if it is overriding the default.
-   *
-   * @var string
-   *
-   * @see EntityFieldQuery::execute().
-   */
-  public $executeCallback = '';
-
-  /**
-   * Adds a condition on entity-generic metadata.
-   *
-   * If the overall query contains only entity conditions or ordering, or if
-   * there are property conditions, then specifying the entity type is
-   * mandatory. If there are field conditions or ordering but no property
-   * conditions or ordering, then specifying an entity type is optional. While
-   * the field storage engine might support field conditions on more than one
-   * entity type, there is no way to query across multiple entity base tables by
-   * default. To specify the entity type, pass in 'entity_type' for $name,
-   * the type as a string for $value, and no $operator (it's disregarded).
-   *
-   * 'bundle', 'revision_id' and 'entity_id' have no such restrictions.
-   *
-   * Note: The "comment" entity type does not support bundle conditions.
-   *
-   * @param $name
-   *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
-   * @param $value
-   *   The value for $name. 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
-   *   Possible values:
-   *   - '=', '<>', '>', '>=', '<', '<=', '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.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function entityCondition($name, $value, $operator = NULL) {
-    // The '!=' operator is deprecated in favour of the '<>' operator since the
-    // latter is ANSI SQL compatible.
-    if ($operator == '!=') {
-      $operator = '<>';
-    }
-    $this->entityConditions[$name] = array(
-      'value' => $value,
-      'operator' => $operator,
-    );
-    return $this;
-  }
-
-  /**
-   * Adds a condition on field values.
-   *
-   * Note that entities with empty field values will be excluded from the
-   * EntityFieldQuery results when using this method.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $column
-   *   The column that should hold the value to be matched.
-   * @param $value
-   *   The value to test the column value against.
-   * @param $operator
-   *   The operator to be used to test the given value.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   *
-   * @see EntityFieldQuery::addFieldCondition
-   * @see EntityFieldQuery::deleted
-   */
-  public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    return $this->addFieldCondition($this->fieldConditions, $field, $column, $value, $operator, $delta_group, $language_group);
-  }
-
-  /**
-   * Adds a condition on the field language column.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $value
-   *   The value to test the column value against.
-   * @param $operator
-   *   The operator to be used to test the given value.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   *
-   * @see EntityFieldQuery::addFieldCondition
-   * @see EntityFieldQuery::deleted
-   */
-  public function fieldLanguageCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    return $this->addFieldCondition($this->fieldMetaConditions, $field, 'language', $value, $operator, $delta_group, $language_group);
-  }
-
-  /**
-   * Adds a condition on the field delta column.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $value
-   *   The value to test the column value against.
-   * @param $operator
-   *   The operator to be used to test the given value.
-   * @param $delta_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $delta_group.
-   * @param $language_group
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   *
-   * @see EntityFieldQuery::addFieldCondition
-   * @see EntityFieldQuery::deleted
-   */
-  public function fieldDeltaCondition($field, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    return $this->addFieldCondition($this->fieldMetaConditions, $field, 'delta', $value, $operator, $delta_group, $language_group);
-  }
-
-  /**
-   * Adds the given condition to the proper condition array.
-   *
-   * @param $conditions
-   *   A reference to an array of conditions.
-   * @param $field
-   *   Either a field name or a field array.
-   * @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.
-   * @param $value
-   *   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
-   *   Possible values:
-   *   - '=', '<>', '>', '>=', '<', '<=', '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
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $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
-   *   An arbitrary identifier: conditions in the same group must have the same
-   *   $language_group.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  protected function addFieldCondition(&$conditions, $field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL) {
-    // The '!=' operator is deprecated in favour of the '<>' operator since the
-    // latter is ANSI SQL compatible.
-    if ($operator == '!=') {
-      $operator = '<>';
-    }
-    if (is_scalar($field)) {
-      $field_definition = field_info_field($field);
-      if (empty($field_definition)) {
-        throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
-      }
-      $field = $field_definition;
-    }
-    // Ensure the same index is used for field conditions as for fields.
-    $index = count($this->fields);
-    $this->fields[$index] = $field;
-    if (isset($column)) {
-      $conditions[$index] = array(
-        'field' => $field,
-        'column' => $column,
-        'value' => $value,
-        'operator' => $operator,
-        'delta_group' => $delta_group,
-        'language_group' => $language_group,
-      );
-    }
-    return $this;
-  }
-
-  /**
-   * Adds a condition on an entity-specific property.
-   *
-   * An $entity_type must be specified by calling
-   * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
-   * executing the query. Also, by default only entities stored in SQL are
-   * supported; however, EntityFieldQuery::executeCallback can be set to handle
-   * different entity storage.
-   *
-   * @param $column
-   *   A column defined in the hook_schema() of the base table of the entity.
-   * @param $value
-   *   The value to test the field 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
-   *   Possible values:
-   *   - '=', '<>', '>', '>=', '<', '<=', '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.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function propertyCondition($column, $value, $operator = NULL) {
-    // The '!=' operator is deprecated in favour of the '<>' operator since the
-    // latter is ANSI SQL compatible.
-    if ($operator == '!=') {
-      $operator = '<>';
-    }
-    $this->propertyConditions[] = array(
-      'column' => $column,
-      'value' => $value,
-      'operator' => $operator,
-    );
-    return $this;
-  }
-
-  /**
-   * Orders the result set by entity-generic metadata.
-   *
-   * If called multiple times, the query will order by each specified column in
-   * the order this method is called.
-   *
-   * Note: The "comment" and "taxonomy_term" entity types don't support ordering
-   * by bundle. For "taxonomy_term", propertyOrderBy('vid') can be used instead.
-   *
-   * @param $name
-   *   'entity_type', 'bundle', 'revision_id' or 'entity_id'.
-   * @param $direction
-   *   The direction to sort. Legal values are "ASC" and "DESC".
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function entityOrderBy($name, $direction = 'ASC') {
-    $this->order[] = array(
-      'type' => 'entity',
-      'specifier' => $name,
-      'direction' => $direction,
-    );
-    return $this;
-  }
-
-  /**
-   * Orders the result set by a given field column.
-   *
-   * If called multiple times, the query will order by each specified column in
-   * the order this method is called. Note that entities with empty field
-   * values will be excluded from the EntityFieldQuery results when using this
-   * method.
-   *
-   * @param $field
-   *   Either a field name or a field array.
-   * @param $column
-   *   A column defined in the hook_field_schema() of this field. entity_id and
-   *   bundle can also be used.
-   * @param $direction
-   *   The direction to sort. Legal values are "ASC" and "DESC".
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function fieldOrderBy($field, $column, $direction = 'ASC') {
-    if (is_scalar($field)) {
-      $field_definition = field_info_field($field);
-      if (empty($field_definition)) {
-        throw new EntityFieldQueryException(t('Unknown field: @field_name', array('@field_name' => $field)));
-      }
-      $field = $field_definition;
-    }
-    // Save the index used for the new field, for later use in field storage.
-    $index = count($this->fields);
-    $this->fields[$index] = $field;
-    $this->order[] = array(
-      'type' => 'field',
-      'specifier' => array(
-        'field' => $field,
-        'index' => $index,
-        'column' => $column,
-      ),
-      'direction' => $direction,
-    );
-    return $this;
-  }
-
-  /**
-   * Orders the result set by an entity-specific property.
-   *
-   * An $entity_type must be specified by calling
-   * EntityFieldCondition::entityCondition('entity_type', $entity_type) before
-   * executing the query.
-   *
-   * If called multiple times, the query will order by each specified column in
-   * the order this method is called.
-   *
-   * @param $column
-   *   The column on which to order.
-   * @param $direction
-   *   The direction to sort. Legal values are "ASC" and "DESC".
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function propertyOrderBy($column, $direction = 'ASC') {
-    $this->order[] = array(
-      'type' => 'property',
-      'specifier' => $column,
-      'direction' => $direction,
-    );
-    return $this;
-  }
-
-  /**
-   * Sets the query to be a count query only.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function count() {
-    $this->count = TRUE;
-    return $this;
-  }
-
-  /**
-   * Restricts a query to a given range in the result set.
-   *
-   * @param $start
-   *   The first entity from the result set to return. If NULL, removes any
-   *   range directives that are set.
-   * @param $length
-   *   The number of entities to return from the result set.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function range($start = NULL, $length = NULL) {
-    $this->range = array(
-      'start' => $start,
-      'length' => $length,
-    );
-    return $this;
-  }
-
-  /**
-   * Enables a pager for the query.
-   *
-   * @param $limit
-   *   An integer specifying the number of elements per page.  If passed a false
-   *   value (FALSE, 0, NULL), the pager is disabled.
-   * @param $element
-   *   An optional integer to distinguish between multiple pagers on one page.
-   *   If not provided, one is automatically calculated.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function pager($limit = 10, $element = NULL) {
-    if (!isset($element)) {
-      $element = PagerDefault::$maxElement++;
-    }
-    elseif ($element >= PagerDefault::$maxElement) {
-      PagerDefault::$maxElement = $element + 1;
-    }
-
-    $this->pager = array(
-      'limit' => $limit,
-      'element' => $element,
-    );
-    return $this;
-  }
-
-  /**
-   * Enables sortable tables for this query.
-   *
-   * @param $headers
-   *   An EFQ Header array based on which the order clause is added to the
-   *   query.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function tableSort(&$headers) {
-    // If 'field' is not initialized, the header columns aren't clickable
-    foreach ($headers as $key =>$header) {
-      if (is_array($header) && isset($header['specifier'])) {
-        $headers[$key]['field'] = '';
-      }
-    }
-
-    $order = tablesort_get_order($headers);
-    $direction = tablesort_get_sort($headers);
-    foreach ($headers as $header) {
-      if (is_array($header) && ($header['data'] == $order['name'])) {
-        if ($header['type'] == 'field') {
-          $this->fieldOrderBy($header['specifier']['field'], $header['specifier']['column'], $direction);
-        }
-        else {
-          $header['direction'] = $direction;
-          $this->order[] = $header;
-        }
-      }
-    }
-
-    return $this;
-  }
-
-  /**
-   * Filters on the data being deleted.
-   *
-   * @param $deleted
-   *   TRUE to only return deleted data, FALSE to return non-deleted data,
-   *   EntityFieldQuery::RETURN_ALL to return everything. Defaults to FALSE.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function deleted($deleted = TRUE) {
-    $this->deleted = $deleted;
-    return $this;
-  }
-
-  /**
-   * Queries the current or every revision.
-   *
-   * Note that this only affects field conditions. Property conditions always
-   * apply to the current revision.
-   * @TODO: Once revision tables have been cleaned up, revisit this.
-   *
-   * @param $age
-   *   - FIELD_LOAD_CURRENT (default): Query the most recent revisions for all
-   *     entities. The results will be keyed by entity type and entity ID.
-   *   - FIELD_LOAD_REVISION: Query all revisions. The results will be keyed by
-   *     entity type and entity revision ID.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function age($age) {
-    $this->age = $age;
-    return $this;
-  }
-
-  /**
-   * Adds a tag to the query.
-   *
-   * Tags are strings that mark a query so that hook_query_alter() and
-   * hook_query_TAG_alter() implementations may decide if they wish to alter
-   * the query. A query may have any number of tags, and they must be valid PHP
-   * identifiers (composed of letters, numbers, and underscores). For example,
-   * queries involving nodes that will be displayed for a user need to add the
-   * tag 'node_access', so that the node module can add access restrictions to
-   * the query.
-   *
-   * If an entity field query has tags, it must also have an entity type
-   * specified, because the alter hook will need the entity base table.
-   *
-   * @param string $tag
-   *   The tag to add.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function addTag($tag) {
-    $this->tags[$tag] = $tag;
-    return $this;
-  }
-
-  /**
-   * Adds additional metadata to the query.
-   *
-   * Sometimes a query may need to provide additional contextual data for the
-   * alter hook. The alter hook implementations may then use that information
-   * to decide if and how to take action.
-   *
-   * @param $key
-   *   The unique identifier for this piece of metadata. Must be a string that
-   *   follows the same rules as any other PHP identifier.
-   * @param $object
-   *   The additional data to add to the query. May be any valid PHP variable.
-   *
-   * @return EntityFieldQuery
-   *   The called object.
-   */
-  public function addMetaData($key, $object) {
-    $this->metaData[$key] = $object;
-    return $this;
-  }
-
-  /**
-   * Executes the query.
-   *
-   * After executing the query, $this->orderedResults will contain a list of
-   * the same stub entities in the order returned by the query. This is only
-   * relevant if there are multiple entity types in the returned value and
-   * a field ordering was requested. In every other case, the returned value
-   * contains everything necessary for processing.
-   *
-   * @return
-   *   Either a number if count() was called or an array of associative arrays
-   *   of stub entities. The outer array keys are entity types, and the inner
-   *   array keys are the relevant ID. (In most cases this will be the entity
-   *   ID. The only exception is when age=FIELD_LOAD_REVISION is used and field
-   *   conditions or sorts are present -- in this case, the key will be the
-   *   revision ID.) The entity type will only exist in the outer array if
-   *   results were found. The inner array values are always stub entities, as
-   *   returned by entity_create_stub_entity(). To traverse the returned array:
-   *   @code
-   *     foreach ($query->execute() as $entity_type => $entities) {
-   *       foreach ($entities as $entity_id => $entity) {
-   *   @endcode
-   *   Note if the entity type is known, then the following snippet will load
-   *   the entities found:
-   *   @code
-   *     $result = $query->execute();
-   *     if (!empty($result[$my_type])) {
-   *       $entities = entity_load($my_type, array_keys($result[$my_type]));
-   *     }
-   *   @endcode
-   */
-  public function execute() {
-    // Give a chance to other modules to alter the query.
-    drupal_alter('entity_query', $this);
-    $this->altered = TRUE;
-
-    // Initialize the pager.
-    $this->initializePager();
-
-    // Execute the query using the correct callback.
-    $result = call_user_func($this->queryCallback(), $this);
-
-    return $result;
-  }
-
-  /**
-   * Determines the query callback to use for this entity query.
-   *
-   * @return
-   *   A callback that can be used with call_user_func().
-   */
-  public function queryCallback() {
-    // Use the override from $this->executeCallback. It can be set either
-    // while building the query, or using hook_entity_query_alter().
-    if (function_exists($this->executeCallback)) {
-      return $this->executeCallback;
-    }
-    // If there are no field conditions and sorts, and no execute callback
-    // then we default to querying entity tables in SQL.
-    if (empty($this->fields)) {
-      return array($this, 'propertyQuery');
-    }
-    // If no override, find the storage engine to be used.
-    foreach ($this->fields as $field) {
-      if (!isset($storage)) {
-        $storage = $field['storage']['module'];
-      }
-      elseif ($storage != $field['storage']['module']) {
-        throw new EntityFieldQueryException(t("Can't handle more than one field storage engine"));
-      }
-    }
-    if ($storage) {
-      // Use hook_field_storage_query() from the field storage.
-      return $storage . '_field_storage_query';
-    }
-    else {
-      throw new EntityFieldQueryException(t("Field storage engine not found."));
-    }
-  }
-
-  /**
-   * Queries entity tables in SQL for property conditions and sorts.
-   *
-   * This method is only used if there are no field conditions and sorts.
-   *
-   * @return
-   *   See EntityFieldQuery::execute().
-   */
-  protected function propertyQuery() {
-    if (empty($this->entityConditions['entity_type'])) {
-      throw new EntityFieldQueryException(t('For this query an entity type must be specified.'));
-    }
-    $entity_type = $this->entityConditions['entity_type']['value'];
-    $entity_info = entity_get_info($entity_type);
-    if (empty($entity_info['base table'])) {
-      throw new EntityFieldQueryException(t('Entity %entity has no base table.', array('%entity' => $entity_type)));
-    }
-    $base_table = $entity_info['base table'];
-    $base_table_schema = drupal_get_schema($base_table);
-    $select_query = db_select($base_table);
-    $select_query->addExpression(':entity_type', 'entity_type', array(':entity_type' => $entity_type));
-    // Process the property conditions.
-    foreach ($this->propertyConditions as $property_condition) {
-      $this->addCondition($select_query, $base_table . '.' . $property_condition['column'], $property_condition);
-    }
-    // Process the four possible entity condition.
-    // The id field is always present in entity keys.
-    $sql_field = $entity_info['entity keys']['id'];
-    $id_map['entity_id'] = $sql_field;
-    $select_query->addField($base_table, $sql_field, 'entity_id');
-    if (isset($this->entityConditions['entity_id'])) {
-      $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['entity_id']);
-    }
-
-    // If there is a revision key defined, use it.
-    if (!empty($entity_info['entity keys']['revision'])) {
-      $sql_field = $entity_info['entity keys']['revision'];
-      $select_query->addField($base_table, $sql_field, 'revision_id');
-      if (isset($this->entityConditions['revision_id'])) {
-        $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['revision_id']);
-      }
-    }
-    else {
-      $sql_field = 'revision_id';
-      $select_query->addExpression('NULL', 'revision_id');
-    }
-    $id_map['revision_id'] = $sql_field;
-
-    // Handle bundles.
-    if (!empty($entity_info['entity keys']['bundle'])) {
-      $sql_field = $entity_info['entity keys']['bundle'];
-      $having = FALSE;
-
-      if (!empty($base_table_schema['fields'][$sql_field])) {
-        $select_query->addField($base_table, $sql_field, 'bundle');
-      }
-    }
-    else {
-      $sql_field = 'bundle';
-      $select_query->addExpression(':bundle', 'bundle', array(':bundle' => $entity_type));
-      $having = TRUE;
-    }
-    $id_map['bundle'] = $sql_field;
-    if (isset($this->entityConditions['bundle'])) {
-      if (!empty($entity_info['entity keys']['bundle'])) {
-        $this->addCondition($select_query, $base_table . '.' . $sql_field, $this->entityConditions['bundle'], $having);
-      }
-      else {
-        // This entity has no bundle, so invalidate the query.
-        $select_query->where('1 = 0');
-      }
-    }
-
-    // Order the query.
-    foreach ($this->order as $order) {
-      if ($order['type'] == 'entity') {
-        $key = $order['specifier'];
-        if (!isset($id_map[$key])) {
-          throw new EntityFieldQueryException(t('Do not know how to order on @key for @entity_type', array('@key' => $key, '@entity_type' => $entity_type)));
-        }
-        $select_query->orderBy($id_map[$key], $order['direction']);
-      }
-      elseif ($order['type'] == 'property') {
-        $select_query->orderBy($base_table . '.' . $order['specifier'], $order['direction']);
-      }
-    }
-
-    return $this->finishQuery($select_query);
-  }
-
-  /**
-   * Gets the total number of results and initializes a pager for the query.
-   *
-   * The pager can be disabled by either setting the pager limit to 0, or by
-   * setting this query to be a count query.
-   */
-  function initializePager() {
-    if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
-      $page = pager_find_page($this->pager['element']);
-      $count_query = clone $this;
-      $this->pager['total'] = $count_query->count()->execute();
-      $this->pager['start'] = $page * $this->pager['limit'];
-      pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
-      $this->range($this->pager['start'], $this->pager['limit']);
-    }
-  }
-
-  /**
-   * Finishes the query.
-   *
-   * Adds tags, metaData, range and returns the requested list or count.
-   *
-   * @param SelectQuery $select_query
-   *   A SelectQuery which has entity_type, entity_id, revision_id and bundle
-   *   fields added.
-   * @param $id_key
-   *   Which field's values to use as the returned array keys.
-   *
-   * @return
-   *   See EntityFieldQuery::execute().
-   */
-  function finishQuery($select_query, $id_key = 'entity_id') {
-    foreach ($this->tags as $tag) {
-      $select_query->addTag($tag);
-    }
-    foreach ($this->metaData as $key => $object) {
-      $select_query->addMetaData($key, $object);
-    }
-    $select_query->addMetaData('entity_field_query', $this);
-    if ($this->range) {
-      $select_query->range($this->range['start'], $this->range['length']);
-    }
-    if ($this->count) {
-      return $select_query->countQuery()->execute()->fetchField();
-    }
-    $return = array();
-    foreach ($select_query->execute() as $partial_entity) {
-      $bundle = isset($partial_entity->bundle) ? $partial_entity->bundle : NULL;
-      $entity = entity_create_stub_entity($partial_entity->entity_type, array($partial_entity->entity_id, $partial_entity->revision_id, $bundle));
-      $return[$partial_entity->entity_type][$partial_entity->$id_key] = $entity;
-      $this->ordered_results[] = $partial_entity;
-    }
-    return $return;
-  }
-
-  /**
-   * Adds a condition to an already built SelectQuery (internal function).
-   *
-   * This is a helper for hook_entity_query() and hook_field_storage_query().
-   *
-   * @param SelectQuery $select_query
-   *   A SelectQuery object.
-   * @param $sql_field
-   *   The name of the field.
-   * @param $condition
-   *   A condition as described in EntityFieldQuery::fieldCondition() and
-   *   EntityFieldQuery::entityCondition().
-   * @param $having
-   *   HAVING or WHERE. This is necessary because SQL can't handle WHERE
-   *   conditions on aliased columns.
-   */
-  public function addCondition(SelectQuery $select_query, $sql_field, $condition, $having = FALSE) {
-    $method = $having ? 'havingCondition' : 'condition';
-    $like_prefix = '';
-    switch ($condition['operator']) {
-      case 'CONTAINS':
-        $like_prefix = '%';
-      case 'STARTS_WITH':
-        $select_query->$method($sql_field, $like_prefix . db_like($condition['value']) . '%', 'LIKE');
-        break;
-      default:
-        $select_query->$method($sql_field, $condition['value'], $condition['operator']);
-    }
-  }
-
-}
-
-/**
- * Defines an exception thrown when a malformed entity is passed.
- */
-class EntityMalformedException extends Exception { }

+ 1 - 1
includes/errors.inc

@@ -9,7 +9,7 @@
  * Maps PHP error constants to watchdog severity levels.
  *
  * The error constants are documented at
- * http://php.net/manual/en/errorfunc.constants.php
+ * http://php.net/manual/errorfunc.constants.php
  *
  * @ingroup logging_severity_levels
  */

+ 85 - 42
includes/file.inc

@@ -470,8 +470,11 @@ function file_ensure_htaccess() {
  * @param $private
  *   FALSE indicates that $directory should be an open and public directory.
  *   The default is TRUE which indicates a private and protected directory.
+ * @param $force_overwrite
+ *   Set to TRUE to attempt to overwrite the existing .htaccess file if one is
+ *   already present. Defaults to FALSE.
  */
-function file_create_htaccess($directory, $private = TRUE) {
+function file_create_htaccess($directory, $private = TRUE, $force_overwrite = FALSE) {
   if (file_uri_scheme($directory)) {
     $directory = file_stream_wrapper_uri_normalize($directory);
   }
@@ -480,19 +483,12 @@ function file_create_htaccess($directory, $private = TRUE) {
   }
   $htaccess_path =  $directory . '/.htaccess';
 
-  if (file_exists($htaccess_path)) {
+  if (file_exists($htaccess_path) && !$force_overwrite) {
     // Short circuit if the .htaccess file already exists.
     return;
   }
 
-  if ($private) {
-    // Private .htaccess file.
-    $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nDeny from all\nOptions None\nOptions +FollowSymLinks";
-  }
-  else {
-    // Public .htaccess file.
-    $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks";
-  }
+  $htaccess_lines = file_htaccess_lines($private);
 
   // Write the .htaccess file.
   if (file_put_contents($htaccess_path, $htaccess_lines)) {
@@ -504,6 +500,45 @@ function file_create_htaccess($directory, $private = TRUE) {
   }
 }
 
+/**
+ * Returns the standard .htaccess lines that Drupal writes to file directories.
+ *
+ * @param $private
+ *   (Optional) Set to FALSE to return the .htaccess lines for an open and
+ *   public directory. The default is TRUE, which returns the .htaccess lines
+ *   for a private and protected directory.
+ *
+ * @return
+ *   A string representing the desired contents of the .htaccess file.
+ *
+ * @see file_create_htaccess()
+ */
+function file_htaccess_lines($private = TRUE) {
+  $lines = <<<EOF
+# Turn off all options we don't need.
+Options None
+Options +FollowSymLinks
+
+# Set the catch-all handler to prevent scripts from being executed.
+SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
+<Files *>
+  # Override the handler again if we're run later in the evaluation list.
+  SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
+</Files>
+
+# If we know how to do it safely, disable the PHP engine entirely.
+<IfModule mod_php5.c>
+  php_flag engine off
+</IfModule>
+EOF;
+
+  if ($private) {
+    $lines = "Deny from all\n\n" . $lines;
+  }
+
+  return $lines;
+}
+
 /**
  * Loads file objects from the database.
  *
@@ -586,7 +621,11 @@ function file_save(stdClass $file) {
     module_invoke_all('entity_update', $file, 'file');
   }
 
+  // Clear internal properties.
   unset($file->original);
+  // Clear the static loading cache.
+  entity_get_controller('file')->resetCache(array($file->fid));
+
   return $file;
 }
 
@@ -719,10 +758,11 @@ function file_usage_delete(stdClass $file, $module, $type = NULL, $id = NULL, $c
  * stored in the database. This is a powerful function that in many ways
  * performs like an advanced version of copy().
  * - Checks if $source and $destination are valid and readable/writable.
- * - Checks that $source is not equal to $destination; if they are an error
- *   is reported.
  * - If file already exists in $destination either the call will error out,
  *   replace the file or rename the file based on the $replace parameter.
+ * - If the $source and $destination are equal, the behavior depends on the
+ *   $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME
+ *   will rename the file until the $destination is unique.
  * - Adds the new file to the files database. If the source file is a
  *   temporary file, the resulting file will also be a temporary file. See
  *   file_save_upload() for details on temporary files.
@@ -817,10 +857,11 @@ function file_valid_uri($uri) {
  * This is a powerful function that in many ways performs like an advanced
  * version of copy().
  * - Checks if $source and $destination are valid and readable/writable.
- * - Checks that $source is not equal to $destination; if they are an error
- *   is reported.
  * - If file already exists in $destination either the call will error out,
  *   replace the file or rename the file based on the $replace parameter.
+ * - If the $source and $destination are equal, the behavior depends on the
+ *   $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME
+ *   will rename the file until the $destination is unique.
  * - Provides a fallback using realpaths if the move fails using stream
  *   wrappers. This can occur because PHP's copy() function does not properly
  *   support streams if safe_mode or open_basedir are enabled. See
@@ -1108,7 +1149,7 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
 
   // Allow potentially insecure uploads for very savvy users and admin
   if (!variable_get('allow_insecure_uploads', 0)) {
-    // Remove any null bytes. See http://php.net/manual/en/security.filesystem.nullbytes.php
+    // Remove any null bytes. See http://php.net/manual/security.filesystem.nullbytes.php
     $filename = str_replace(chr(0), '', $filename);
 
     $whitelist = array_unique(explode(' ', trim($extensions)));
@@ -1256,6 +1297,7 @@ function file_delete(stdClass $file, $force = FALSE) {
   if (file_unmanaged_delete($file->uri)) {
     db_delete('file_managed')->condition('fid', $file->fid)->execute();
     db_delete('file_usage')->condition('fid', $file->fid)->execute();
+    entity_get_controller('file')->resetCache();
     return TRUE;
   }
   return FALSE;
@@ -1365,8 +1407,9 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) {
  * Temporary files are periodically cleaned. To make the file a permanent file,
  * assign the status and use file_save() to save the changes.
  *
- * @param $source
- *   A string specifying the filepath or URI of the uploaded file to save.
+ * @param $form_field_name
+ *   A string that is the associative array key of the upload form element in
+ *   the form array.
  * @param $validators
  *   An optional, associative array of callback functions used to validate the
  *   file. See file_validate() for a full discussion of the array format.
@@ -1377,9 +1420,9 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) {
  *   (Beware: this is not safe and should only be allowed for trusted users, if
  *   at all).
  * @param $destination
- *   A string containing the URI $source should be copied to.
- *   This must be a stream wrapper URI. If this value is omitted, Drupal's
- *   temporary files scheme will be used ("temporary://").
+ *   A string containing the URI that the file should be copied to. This must
+ *   be a stream wrapper URI. If this value is omitted, Drupal's temporary
+ *   files scheme will be used ("temporary://").
  * @param $replace
  *   Replace behavior when the destination file already exists:
  *   - FILE_EXISTS_REPLACE: Replace the existing file.
@@ -1397,45 +1440,45 @@ function file_space_used($uid = NULL, $status = FILE_STATUS_PERMANENT) {
  *   - source: Path to the file before it is moved.
  *   - destination: Path to the file after it is moved (same as 'uri').
  */
-function file_save_upload($source, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
+function file_save_upload($form_field_name, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
   global $user;
   static $upload_cache;
 
   // Return cached objects without processing since the file will have
   // already been processed and the paths in _FILES will be invalid.
-  if (isset($upload_cache[$source])) {
-    return $upload_cache[$source];
+  if (isset($upload_cache[$form_field_name])) {
+    return $upload_cache[$form_field_name];
   }
 
   // Make sure there's an upload to process.
-  if (empty($_FILES['files']['name'][$source])) {
+  if (empty($_FILES['files']['name'][$form_field_name])) {
     return NULL;
   }
 
   // Check for file upload errors and return FALSE if a lower level system
   // error occurred. For a complete list of errors:
-  // See http://php.net/manual/en/features.file-upload.errors.php.
-  switch ($_FILES['files']['error'][$source]) {
+  // See http://php.net/manual/features.file-upload.errors.php.
+  switch ($_FILES['files']['error'][$form_field_name]) {
     case UPLOAD_ERR_INI_SIZE:
     case UPLOAD_ERR_FORM_SIZE:
-      drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $_FILES['files']['name'][$source], '%maxsize' => format_size(file_upload_max_size()))), 'error');
+      drupal_set_message(t('The file %file could not be saved, because it exceeds %maxsize, the maximum allowed size for uploads.', array('%file' => $_FILES['files']['name'][$form_field_name], '%maxsize' => format_size(file_upload_max_size()))), 'error');
       return FALSE;
 
     case UPLOAD_ERR_PARTIAL:
     case UPLOAD_ERR_NO_FILE:
-      drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $_FILES['files']['name'][$source])), 'error');
+      drupal_set_message(t('The file %file could not be saved, because the upload did not complete.', array('%file' => $_FILES['files']['name'][$form_field_name])), 'error');
       return FALSE;
 
     case UPLOAD_ERR_OK:
       // Final check that this is a valid upload, if it isn't, use the
       // default error handler.
-      if (is_uploaded_file($_FILES['files']['tmp_name'][$source])) {
+      if (is_uploaded_file($_FILES['files']['tmp_name'][$form_field_name])) {
         break;
       }
 
     // Unknown error
     default:
-      drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$source])), 'error');
+      drupal_set_message(t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $_FILES['files']['name'][$form_field_name])), 'error');
       return FALSE;
   }
 
@@ -1443,10 +1486,10 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
   $file = new stdClass();
   $file->uid      = $user->uid;
   $file->status   = 0;
-  $file->filename = trim(drupal_basename($_FILES['files']['name'][$source]), '.');
-  $file->uri      = $_FILES['files']['tmp_name'][$source];
+  $file->filename = trim(drupal_basename($_FILES['files']['name'][$form_field_name]), '.');
+  $file->uri      = $_FILES['files']['tmp_name'][$form_field_name];
   $file->filemime = file_get_mimetype($file->filename);
-  $file->filesize = $_FILES['files']['size'][$source];
+  $file->filesize = $_FILES['files']['size'][$form_field_name];
 
   $extensions = '';
   if (isset($validators['file_validate_extensions'])) {
@@ -1503,7 +1546,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
     return FALSE;
   }
 
-  $file->source = $source;
+  $file->source = $form_field_name;
   // A URI may already have a trailing slash or look like "public://".
   if (substr($destination, -1) != '/') {
     $destination .= '/';
@@ -1512,7 +1555,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
   // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
   // there's an existing file so we need to bail.
   if ($file->destination === FALSE) {
-    drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $source, '%directory' => $destination)), 'error');
+    drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $form_field_name, '%directory' => $destination)), 'error');
     return FALSE;
   }
 
@@ -1531,7 +1574,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
     else {
       $message .= ' ' . array_pop($errors);
     }
-    form_set_error($source, $message);
+    form_set_error($form_field_name, $message);
     return FALSE;
   }
 
@@ -1539,8 +1582,8 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
   // directory. This overcomes open_basedir restrictions for future file
   // operations.
   $file->uri = $file->destination;
-  if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$source], $file->uri)) {
-    form_set_error($source, t('File upload error. Could not move uploaded file.'));
+  if (!drupal_move_uploaded_file($_FILES['files']['tmp_name'][$form_field_name], $file->uri)) {
+    form_set_error($form_field_name, t('File upload error. Could not move uploaded file.'));
     watchdog('file', 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->filename, '%destination' => $file->uri));
     return FALSE;
   }
@@ -1560,7 +1603,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
   // If we made it this far it's safe to record this file in the database.
   if ($file = file_save($file)) {
     // Add file to the cache.
-    $upload_cache[$source] = $file;
+    $upload_cache[$form_field_name] = $file;
     return $file;
   }
   return FALSE;
@@ -2177,7 +2220,7 @@ function drupal_chmod($uri, $mode = NULL) {
  * @param $uri
  *   A URI or pathname.
  * @param $context
- *   Refer to http://php.net/manual/en/ref.stream.php
+ *   Refer to http://php.net/manual/ref.stream.php
  *
  * @return
  *   Boolean TRUE on success, or FALSE on failure.
@@ -2310,7 +2353,7 @@ function drupal_basename($uri, $suffix = NULL) {
  * @param $recursive
  *   Default to FALSE.
  * @param $context
- *   Refer to http://php.net/manual/en/ref.stream.php
+ *   Refer to http://php.net/manual/ref.stream.php
  *
  * @return
  *   Boolean TRUE on success, or FALSE on failure.
@@ -2341,7 +2384,7 @@ function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
  * @param $uri
  *   A URI or pathname.
  * @param $context
- *   Refer to http://php.net/manual/en/ref.stream.php
+ *   Refer to http://php.net/manual/ref.stream.php
  *
  * @return
  *   Boolean TRUE on success, or FALSE on failure.

+ 3 - 3
includes/filetransfer/ftp.inc

@@ -82,11 +82,11 @@ class FileTransferFTPExtension extends FileTransferFTP implements FileTransferCh
     if (!$list) {
       $list = array();
     }
-    foreach ($list as $item){
+    foreach ($list as $item) {
       if ($item == '.' || $item == '..') {
         continue;
       }
-      if (@ftp_chdir($this->connection, $item)){
+      if (@ftp_chdir($this->connection, $item)) {
         ftp_cdup($this->connection);
         $this->removeDirectory(ftp_pwd($this->connection) . '/' . $item);
       }
@@ -122,7 +122,7 @@ class FileTransferFTPExtension extends FileTransferFTP implements FileTransferCh
 
   function chmodJailed($path, $mode, $recursive) {
     if (!ftp_chmod($this->connection, $mode, $path)) {
-      throw new FileTransferException("Unable to set permissions on %file", NULL, array ('%file' => $path));
+      throw new FileTransferException("Unable to set permissions on %file", NULL, array('%file' => $path));
     }
     if ($this->isDirectory($path) && $recursive) {
       $filelist = @ftp_nlist($this->connection, $path);

+ 70 - 21
includes/form.inc

@@ -15,10 +15,9 @@
  * reference the form builder function using \@see. For examples, of this see
  * system_modules_uninstall() or user_pass(), the latter of which has the
  * following in its doxygen documentation:
- *
- * \@ingroup forms
- * \@see user_pass_validate().
- * \@see user_pass_submit().
+ * - \@ingroup forms
+ * - \@see user_pass_validate()
+ * - \@see user_pass_submit()
  *
  * @}
  */
@@ -168,6 +167,12 @@ function drupal_get_form($form_id) {
  *       processed.
  *     - base_form_id: Identification for a base form, as declared in a
  *       hook_forms() implementation.
+ *     - immutable: If this flag is set to TRUE, a new form build id is
+ *       generated when the form is loaded from the cache. If it is subsequently
+ *       saved to the cache again, it will have another cache id and therefore
+ *       the original form and form-state will remain unaltered. This is
+ *       important when page caching is enabled in order to prevent form state
+ *       from leaking between anonymous users.
  *   - rebuild_info: Internal. Similar to 'build_info', but pertaining to
  *     drupal_rebuild_form().
  *   - rebuild: Normally, after the entire form processing is completed and
@@ -235,6 +240,12 @@ function drupal_get_form($form_id) {
  *     likely to occur during Ajax operations.
  *   - programmed: If TRUE, the form was submitted programmatically, usually
  *     invoked via drupal_form_submit(). Defaults to FALSE.
+ *   - programmed_bypass_access_check: If TRUE, programmatic form submissions
+ *     are processed without taking #access into account. Set this to FALSE
+ *     when submitting a form programmatically with values that may have been
+ *     input by the user executing the current request; this will cause #access
+ *     to be respected as it would on a normal form submission. Defaults to
+ *     TRUE.
  *   - process_input: Boolean flag. TRUE signifies correct form submission.
  *     This is always TRUE for programmed forms coming from drupal_form_submit()
  *     (see 'programmed' key), or if the form_id coming from the $_POST data is
@@ -402,6 +413,7 @@ function form_state_defaults() {
     'submitted' => FALSE,
     'executed' => FALSE,
     'programmed' => FALSE,
+    'programmed_bypass_access_check' => TRUE,
     'cache'=> FALSE,
     'method' => 'post',
     'groups' => array(),
@@ -452,17 +464,25 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
   $form = drupal_retrieve_form($form_id, $form_state);
 
   // If only parts of the form will be returned to the browser (e.g., Ajax or
-  // RIA clients), re-use the old #build_id to not require client-side code to
-  // manually update the hidden 'build_id' input element.
+  // RIA clients), or if the form already had a new build ID regenerated when it
+  // was retrieved from the form cache, reuse the existing #build_id.
   // Otherwise, a new #build_id is generated, to not clobber the previous
   // build's data in the form cache; also allowing the user to go back to an
   // earlier build, make changes, and re-submit.
   // @see drupal_prepare_form()
-  if (isset($old_form['#build_id']) && !empty($form_state['rebuild_info']['copy']['#build_id'])) {
+  $enforce_old_build_id = isset($old_form['#build_id']) && !empty($form_state['rebuild_info']['copy']['#build_id']);
+  $old_form_is_mutable_copy = isset($old_form['#build_id_old']);
+  if ($enforce_old_build_id || $old_form_is_mutable_copy) {
     $form['#build_id'] = $old_form['#build_id'];
+    if ($old_form_is_mutable_copy) {
+      $form['#build_id_old'] = $old_form['#build_id_old'];
+    }
   }
   else {
-    $form['#build_id'] = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand());
+    if (isset($old_form['#build_id'])) {
+      $form['#build_id_old'] = $old_form['#build_id'];
+    }
+    $form['#build_id'] = 'form-' . drupal_random_key();
   }
 
   // #action defaults to request_uri(), but in case of Ajax and other partial
@@ -516,6 +536,15 @@ function form_get_cache($form_build_id, &$form_state) {
           }
         }
       }
+      // Generate a new #build_id if the cached form was rendered on a cacheable
+      // page.
+      if (!empty($form_state['build_info']['immutable'])) {
+        $form['#build_id_old'] = $form['#build_id'];
+        $form['#build_id'] = 'form-' . drupal_random_key();
+        $form['form_build_id']['#value'] = $form['#build_id'];
+        $form['form_build_id']['#id'] = $form['#build_id'];
+        unset($form_state['build_info']['immutable']);
+      }
       return $form;
     }
   }
@@ -528,15 +557,28 @@ function form_set_cache($form_build_id, $form, $form_state) {
   // 6 hours cache life time for forms should be plenty.
   $expire = 21600;
 
+  // Ensure that the form build_id embedded in the form structure is the same as
+  // the one passed in as a parameter. This is an additional safety measure to
+  // prevent legacy code operating directly with form_get_cache and
+  // form_set_cache from accidentally overwriting immutable form state.
+  if ($form['#build_id'] != $form_build_id) {
+    watchdog('form', 'Form build-id mismatch detected while attempting to store a form in the cache.', array(), WATCHDOG_ERROR);
+    return;
+  }
+
   // Cache form structure.
   if (isset($form)) {
     if ($GLOBALS['user']->uid) {
       $form['#cache_token'] = drupal_get_token();
     }
+    unset($form['#build_id_old']);
     cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire);
   }
 
   // Cache form state.
+  if (variable_get('cache', 0) && drupal_page_is_cacheable()) {
+    $form_state['build_info']['immutable'] = TRUE;
+  }
   if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
     cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire);
   }
@@ -977,7 +1019,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
   // @see drupal_build_form()
   // @see drupal_rebuild_form()
   if (!isset($form['#build_id'])) {
-    $form['#build_id'] = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand());
+    $form['#build_id'] = 'form-' . drupal_random_key();
   }
   $form['form_build_id'] = array(
     '#type' => 'hidden',
@@ -1129,6 +1171,12 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
 
       // 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)));
+
+      // Stop here and don't run any further validation handlers, because they
+      // could invoke non-safe operations which opens the door for CSRF
+      // vulnerabilities.
+      $validated_forms[$form_id] = TRUE;
+      return;
     }
   }
 
@@ -1979,7 +2027,7 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
   // #access=FALSE on an element usually allow access for some users, so forms
   // submitted with drupal_form_submit() may bypass access restriction and be
   // treated as high-privilege users instead.
-  $process_input = empty($element['#disabled']) && ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access'])));
+  $process_input = empty($element['#disabled']) && (($form_state['programmed'] && $form_state['programmed_bypass_access_check']) || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access'])));
 
   // Set the element's #value property.
   if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
@@ -3052,8 +3100,7 @@ function form_process_radios($element) {
  * @param $variables
  *   An associative array containing:
  *   - element: An associative array containing the properties of the element.
- *     Properties used: #title, #value, #return_value, #description, #required,
- *     #attributes, #checked.
+ *     Properties used: #id, #name, #attributes, #checked, #return_value.
  *
  * @ingroup themeable
  */
@@ -4245,7 +4292,7 @@ function element_validate_number($element, &$form_state) {
  * returns any user input in the 'results' or 'message' keys of $context,
  * it must also sanitize them first.
  *
- * Sample batch operations:
+ * Sample callback_batch_operation():
  * @code
  * // Simple and artificial: load a node of a given type for a given user
  * function my_function_1($uid, $type, &$context) {
@@ -4297,7 +4344,7 @@ function element_validate_number($element, &$form_state) {
  * }
  * @endcode
  *
- * Sample 'finished' callback:
+ * Sample callback_batch_finished():
  * @code
  * function batch_test_finished($success, $results, $operations) {
  *   // The 'success' parameter means no fatal PHP errors were detected. All
@@ -4336,12 +4383,14 @@ function element_validate_number($element, &$form_state) {
  * @param $batch_definition
  *   An associative array defining the batch, with the following elements (all
  *   are optional except as noted):
- *   - operations: (required) Array of function calls to be performed.
+ *   - operations: (required) Array of operations to be performed, where each
+ *     item is an array consisting of the name of an implementation of
+ *     callback_batch_operation() and an array of parameter.
  *     Example:
  *     @code
  *     array(
- *       array('my_function_1', array($arg1)),
- *       array('my_function_2', array($arg2_1, $arg2_2)),
+ *       array('callback_batch_operation_1', array($arg1)),
+ *       array('callback_batch_operation_2', array($arg2_1, $arg2_2)),
  *     )
  *     @endcode
  *   - title: A safe, translated string to use as the title for the progress
@@ -4353,10 +4402,10 @@ function element_validate_number($element, &$form_state) {
  *     @elapsed. Defaults to t('Completed @current of @total.').
  *   - error_message: Message displayed if an error occurred while processing
  *     the batch. Defaults to t('An error has occurred.').
- *   - finished: Name of a function to be executed after the batch has
- *     completed. This should be used to perform any result massaging that may
- *     be needed, and possibly save data in $_SESSION for display after final
- *     page redirection.
+ *   - finished: Name of an implementation of callback_batch_finished(). This is
+ *     executed after the batch has completed. This should be used to perform
+ *     any result massaging that may be needed, and possibly save data in
+ *     $_SESSION for display after final page redirection.
  *   - file: Path to the file containing the definitions of the 'operations' and
  *     'finished' functions, for instance if they don't reside in the main
  *     .module file. The path should be relative to base_path(), and thus should

+ 25 - 1
includes/install.core.inc

@@ -692,6 +692,21 @@ function install_full_redirect_url($install_state) {
  */
 function install_display_output($output, $install_state) {
   drupal_page_header();
+
+  // Prevent install.php from being indexed when installed in a sub folder.
+  // robots.txt rules are not read if the site is within domain.com/subfolder
+  // resulting in /subfolder/install.php being found through search engines.
+  // When settings.php is writeable this can be used via an external database
+  // leading a malicious user to gain php access to the server.
+  $noindex_meta_tag = array(
+    '#tag' => 'meta',
+    '#attributes' => array(
+      'name' => 'robots',
+      'content' => 'noindex, nofollow',
+    ),
+  );
+  drupal_add_html_head($noindex_meta_tag, 'install_meta_robots');
+
   // Only show the task list if there is an active task; otherwise, the page
   // request has ended before tasks have even been started, so there is nothing
   // meaningful to show.
@@ -766,6 +781,15 @@ function install_system_module(&$install_state) {
   // Install system.module.
   drupal_install_system();
 
+  // Call file_ensure_htaccess() to ensure that all of Drupal's standard
+  // directories (e.g., the public and private files directories) have
+  // appropriate .htaccess files. These directories will have already been
+  // created by this point in the installer, since Drupal creates them during
+  // the install_verify_requirements() task. Note that we cannot call
+  // file_ensure_htaccess() any earlier than this, since it relies on
+  // system.module in order to work.
+  file_ensure_htaccess();
+
   // Enable the user module so that sessions can be recorded during the
   // upcoming bootstrap step.
   module_enable(array('user'), FALSE);
@@ -981,7 +1005,7 @@ function install_settings_form_submit($form, &$form_state) {
     'required' => TRUE,
   );
   $settings['drupal_hash_salt'] = array(
-    'value'    => drupal_hash_base64(drupal_random_bytes(55)),
+    'value'    => drupal_random_key(),
     'required' => TRUE,
   );
   drupal_rewrite_settings($settings);

+ 0 - 1
includes/install.inc

@@ -1134,7 +1134,6 @@ function st($string, array $args = array(), array $options = array()) {
     }
   }
 
-  require_once DRUPAL_ROOT . '/includes/theme.inc';
   // Transform arguments before inserting them
   foreach ($args as $key => $value) {
     switch ($key[0]) {

+ 4 - 1
includes/iso.inc

@@ -53,6 +53,7 @@ function _country_get_predefined_list() {
     'BM' => $t('Bermuda'),
     'BN' => $t('Brunei'),
     'BO' => $t('Bolivia'),
+    'BQ' => $t('Caribbean Netherlands'),
     'BR' => $t('Brazil'),
     'BS' => $t('Bahamas'),
     'BT' => $t('Bhutan'),
@@ -74,8 +75,8 @@ function _country_get_predefined_list() {
     'CO' => $t('Colombia'),
     'CR' => $t('Costa Rica'),
     'CU' => $t('Cuba'),
-    'CW' => $t('Curaçao'),
     'CV' => $t('Cape Verde'),
+    'CW' => $t('Curaçao'),
     'CX' => $t('Christmas Island'),
     'CY' => $t('Cyprus'),
     'CZ' => $t('Czech Republic'),
@@ -230,8 +231,10 @@ function _country_get_predefined_list() {
     'SN' => $t('Senegal'),
     'SO' => $t('Somalia'),
     'SR' => $t('Suriname'),
+    'SS' => $t('South Sudan'),
     'ST' => $t('Sao Tome and Principe'),
     'SV' => $t('El Salvador'),
+    'SX' => $t('Sint Maarten'),
     'SY' => $t('Syria'),
     'SZ' => $t('Swaziland'),
     'TC' => $t('Turks and Caicos Islands'),

+ 2 - 3
includes/language.inc

@@ -78,7 +78,7 @@ define('LANGUAGE_NEGOTIATION_DEFAULT', 'language-default');
  * function mymodule_language_negotiation_info_alter(&$negotiation_info) {
  *   // Replace the core function with our own function.
  *   module_load_include('language', 'inc', 'language.negotiation');
- *   $negotiation_info[LANGUAGE_NEGOTIATION_URL]['callbacks']['negotiation'] = 'mymodule_from_url';
+ *   $negotiation_info[LANGUAGE_NEGOTIATION_URL]['callbacks']['language'] = 'mymodule_from_url';
  *   $negotiation_info[LANGUAGE_NEGOTIATION_URL]['file'] = drupal_get_path('module', 'mymodule') . '/mymodule.module';
  * }
  *
@@ -94,7 +94,6 @@ define('LANGUAGE_NEGOTIATION_DEFAULT', 'language-default');
  *   }
  *   return $langcode;
  * }
- * ?>
  * @endcode
  *
  * For more information, see
@@ -314,7 +313,7 @@ function language_negotiation_get_switch_links($type, $path) {
 }
 
 /**
- * Removes any unused language negotation providers from the configuration.
+ * Removes any unused language negotiation providers from the configuration.
  */
 function language_negotiation_purge() {
   // Ensure that we are getting the defined language negotiation information. An

+ 7 - 6
includes/mail.inc

@@ -339,13 +339,13 @@ interface MailSystemInterface {
  *
  * We deliberately use LF rather than CRLF, see drupal_mail().
  *
- * @param $text
+ * @param string $text
  *   The plain text to process.
- * @param $indent (optional)
+ * @param string $indent (optional)
  *   A string to indent the text with. Only '>' characters are repeated on
  *   subsequent wrapped lines. Others are replaced by spaces.
  *
- * @return
+ * @return string
  *   The content of the email as a string with formatting applied.
  */
 function drupal_wrap_mail($text, $indent = '') {
@@ -356,8 +356,9 @@ function drupal_wrap_mail($text, $indent = '') {
   $soft = strpos($clean_indent, ' ') === FALSE;
   // Check if the string has line breaks.
   if (strpos($text, "\n") !== FALSE) {
-    // Remove trailing spaces to make existing breaks hard.
-    $text = preg_replace('/ +\n/m', "\n", $text);
+    // Remove trailing spaces to make existing breaks hard, but leave signature
+    // marker untouched (RFC 3676, Section 4.3).
+    $text = preg_replace('/(?(?<!^--) +\n|  +\n)/m', "\n", $text);
     // Wrap each line at the needed width.
     $lines = explode("\n", $text);
     array_walk($lines, '_drupal_wrap_mail_line', array('soft' => $soft, 'length' => strlen($indent)));
@@ -563,7 +564,7 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
  */
 function _drupal_wrap_mail_line(&$line, $key, $values) {
   // 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.
   $line = wordwrap($line, 996 - $values['length'], $values['soft'] ? " \n" : "\n");
 }

+ 16 - 8
includes/menu.inc

@@ -1000,7 +1000,7 @@ function menu_tree($menu_name) {
 }
 
 /**
- * Returns a rendered menu tree.
+ * Returns an output structure for rendering a menu tree.
  *
  * The menu item's LI element is given one of the following classes:
  * - expanded: The menu item is showing its submenu.
@@ -1926,13 +1926,21 @@ function menu_local_tasks($level = 0) {
     }
 
     // Get all tabs (also known as local tasks) and the root page.
-    $result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC))
-      ->fields('menu_router')
-      ->condition('tab_root', $router_item['tab_root'])
-      ->condition('context', MENU_CONTEXT_INLINE, '<>')
-      ->orderBy('weight')
-      ->orderBy('title')
-      ->execute();
+    $cid = 'local_tasks:' . $router_item['tab_root'];
+    if ($cache = cache_get($cid, 'cache_menu')) {
+      $result = $cache->data;
+    }
+    else {
+      $result = db_select('menu_router', NULL, array('fetch' => PDO::FETCH_ASSOC))
+        ->fields('menu_router')
+        ->condition('tab_root', $router_item['tab_root'])
+        ->condition('context', MENU_CONTEXT_INLINE, '<>')
+        ->orderBy('weight')
+        ->orderBy('title')
+        ->execute()
+        ->fetchAll();
+      cache_set($cid, $result, 'cache_menu');
+    }
     $map = $router_item['original_map'];
     $children = array();
     $tasks = array();

+ 2 - 2
includes/path.inc

@@ -560,8 +560,8 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) {
   elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) {
     // Path is dynamic (ie 'user/%'), so check directly against menu_router table.
     if ($item = db_query("SELECT * FROM {menu_router} where path = :path", array(':path' => $path))->fetchAssoc()) {
-      $item['link_path']  = $form_item['link_path'];
-      $item['link_title'] = $form_item['link_title'];
+      $item['link_path']  = $item['path'];
+      $item['link_title'] = $item['title'];
       $item['external']   = FALSE;
       $item['options'] = '';
       _menu_link_translate($item);

+ 6 - 3
includes/registry.inc

@@ -10,7 +10,7 @@
  * @{
  * The code registry engine.
  *
- * Drupal maintains an internal registry of all functions or classes in the
+ * Drupal maintains an internal registry of all interfaces or classes in the
  * system, allowing it to lazy-load code files as needed (reducing the amount
  * of code that must be parsed on each request).
  */
@@ -120,7 +120,10 @@ function registry_get_parsed_files() {
 }
 
 /**
- * Parse all files that have changed since the registry was last built, and save their function and class listings.
+ * Parse all changed files and save their interface and class listings.
+ *
+ * Parse all files that have changed since the registry was last built, and save
+ * their interface and class listings.
  *
  * @param $files
  *  The list of files to check and parse.
@@ -149,7 +152,7 @@ function _registry_parse_files($files) {
 }
 
 /**
- * Parse a file and save its function and class listings.
+ * Parse a file and save its interface and class listings.
  *
  * @param $filename
  *   Name of the file we are going to parse.

+ 4 - 4
includes/session.inc

@@ -263,10 +263,10 @@ function drupal_session_initialize() {
     // Less random sessions (which are much faster to generate) are used for
     // anonymous users than are generated in drupal_session_regenerate() when
     // a user becomes authenticated.
-    session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE)));
+    session_id(drupal_random_key());
     if ($is_https && variable_get('https', FALSE)) {
       $insecure_session_name = substr(session_name(), 1);
-      $session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE));
+      $session_id = drupal_random_key();
       $_COOKIE[$insecure_session_name] = $session_id;
     }
   }
@@ -360,7 +360,7 @@ function drupal_session_regenerate() {
       $old_insecure_session_id = $_COOKIE[$insecure_session_name];
     }
     $params = session_get_cookie_params();
-    $session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
+    $session_id = drupal_random_key();
     // If a session cookie lifetime is set, the session will expire
     // $params['lifetime'] seconds from the current request. If it is not set,
     // it will expire when the browser is closed.
@@ -372,7 +372,7 @@ function drupal_session_regenerate() {
   if (drupal_session_started()) {
     $old_session_id = session_id();
   }
-  session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55)));
+  session_id(drupal_random_key());
 
   if (isset($old_session_id)) {
     $params = session_get_cookie_params();

+ 20 - 22
includes/stream_wrappers.inc

@@ -93,7 +93,7 @@ define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_N
 /**
  * Generic PHP stream wrapper interface.
  *
- * @see http://www.php.net/manual/en/class.streamwrapper.php
+ * @see http://www.php.net/manual/class.streamwrapper.php
  */
 interface StreamWrapperInterface {
   public function stream_open($uri, $mode, $options, &$opened_url);
@@ -401,7 +401,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   Returns TRUE if file was opened successfully.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-open.php
+   * @see http://php.net/manual/streamwrapper.stream-open.php
    */
   public function stream_open($uri, $mode, $options, &$opened_path) {
     $this->uri = $uri;
@@ -429,7 +429,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   Always returns TRUE at the present time.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-lock.php
+   * @see http://php.net/manual/streamwrapper.stream-lock.php
    */
   public function stream_lock($operation) {
     if (in_array($operation, array(LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB))) {
@@ -448,7 +448,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   The string that was read, or FALSE in case of an error.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-read.php
+   * @see http://php.net/manual/streamwrapper.stream-read.php
    */
   public function stream_read($count) {
     return fread($this->handle, $count);
@@ -463,7 +463,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   The number of bytes written (integer).
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-write.php
+   * @see http://php.net/manual/streamwrapper.stream-write.php
    */
   public function stream_write($data) {
     return fwrite($this->handle, $data);
@@ -475,7 +475,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if end-of-file has been reached.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-eof.php
+   * @see http://php.net/manual/streamwrapper.stream-eof.php
    */
   public function stream_eof() {
     return feof($this->handle);
@@ -492,7 +492,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE on success.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-seek.php
+   * @see http://php.net/manual/streamwrapper.stream-seek.php
    */
   public function stream_seek($offset, $whence) {
     // fseek returns 0 on success and -1 on a failure.
@@ -506,7 +506,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if data was successfully stored (or there was no data to store).
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-flush.php
+   * @see http://php.net/manual/streamwrapper.stream-flush.php
    */
   public function stream_flush() {
     return fflush($this->handle);
@@ -518,7 +518,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   The current offset in bytes from the beginning of file.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-tell.php
+   * @see http://php.net/manual/streamwrapper.stream-tell.php
    */
   public function stream_tell() {
     return ftell($this->handle);
@@ -531,7 +531,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    *   An array with file status, or FALSE in case of an error - see fstat()
    *   for a description of this array.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-stat.php
+   * @see http://php.net/manual/streamwrapper.stream-stat.php
    */
   public function stream_stat() {
     return fstat($this->handle);
@@ -543,7 +543,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if stream was successfully closed.
    *
-   * @see http://php.net/manual/en/streamwrapper.stream-close.php
+   * @see http://php.net/manual/streamwrapper.stream-close.php
    */
   public function stream_close() {
     return fclose($this->handle);
@@ -558,7 +558,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if resource was successfully deleted.
    *
-   * @see http://php.net/manual/en/streamwrapper.unlink.php
+   * @see http://php.net/manual/streamwrapper.unlink.php
    */
   public function unlink($uri) {
     $this->uri = $uri;
@@ -576,7 +576,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if file was successfully renamed.
    *
-   * @see http://php.net/manual/en/streamwrapper.rename.php
+   * @see http://php.net/manual/streamwrapper.rename.php
    */
   public function rename($from_uri, $to_uri) {
     return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
@@ -622,7 +622,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if directory was successfully created.
    *
-   * @see http://php.net/manual/en/streamwrapper.mkdir.php
+   * @see http://php.net/manual/streamwrapper.mkdir.php
    */
   public function mkdir($uri, $mode, $options) {
     $this->uri = $uri;
@@ -654,7 +654,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE if directory was successfully removed.
    *
-   * @see http://php.net/manual/en/streamwrapper.rmdir.php
+   * @see http://php.net/manual/streamwrapper.rmdir.php
    */
   public function rmdir($uri, $options) {
     $this->uri = $uri;
@@ -678,7 +678,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    *   An array with file status, or FALSE in case of an error - see fstat()
    *   for a description of this array.
    *
-   * @see http://php.net/manual/en/streamwrapper.url-stat.php
+   * @see http://php.net/manual/streamwrapper.url-stat.php
    */
   public function url_stat($uri, $flags) {
     $this->uri = $uri;
@@ -704,7 +704,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE on success.
    *
-   * @see http://php.net/manual/en/streamwrapper.dir-opendir.php
+   * @see http://php.net/manual/streamwrapper.dir-opendir.php
    */
   public function dir_opendir($uri, $options) {
     $this->uri = $uri;
@@ -719,7 +719,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   The next filename, or FALSE if there are no more files in the directory.
    *
-   * @see http://php.net/manual/en/streamwrapper.dir-readdir.php
+   * @see http://php.net/manual/streamwrapper.dir-readdir.php
    */
   public function dir_readdir() {
     return readdir($this->handle);
@@ -731,7 +731,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE on success.
    *
-   * @see http://php.net/manual/en/streamwrapper.dir-rewinddir.php
+   * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
    */
   public function dir_rewinddir() {
     rewinddir($this->handle);
@@ -747,7 +747,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
    * @return
    *   TRUE on success.
    *
-   * @see http://php.net/manual/en/streamwrapper.dir-closedir.php
+   * @see http://php.net/manual/streamwrapper.dir-closedir.php
    */
   public function dir_closedir() {
     closedir($this->handle);
@@ -788,8 +788,6 @@ class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper {
  *
  * Provides support for storing privately accessible files with the Drupal file
  * interface.
- *
- * Extends DrupalPublicStreamWrapper.
  */
 class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper {
   /**

+ 7 - 0
misc/ajax.js

@@ -616,6 +616,13 @@ Drupal.ajax.prototype.commands = {
       .removeClass('odd even')
       .filter(':even').addClass('odd').end()
       .filter(':odd').addClass('even');
+  },
+
+  /**
+   * Command to update a form's build ID.
+   */
+  updateBuildId: function(ajax, response, status) {
+    $('input[name="form_build_id"][value="' + response['old'] + '"]').val(response['new']);
   }
 };
 

+ 1 - 1
misc/states.js

@@ -373,7 +373,7 @@ states.Trigger.states = {
 
   checked: {
     'change': function () {
-      return this.attr('checked');
+      return this.is(':checked');
     }
   },
 

+ 3 - 3
modules/aggregator/aggregator.info

@@ -7,8 +7,8 @@ files[] = aggregator.test
 configure = admin/config/services/aggregator/settings
 stylesheets[all][] = aggregator.css
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 10 - 0
modules/aggregator/aggregator.install

@@ -260,6 +260,7 @@ function aggregator_schema() {
     'primary key' => array('iid'),
     'indexes' => array(
       'fid' => array('fid'),
+      'timestamp' => array('timestamp'),
     ),
     'foreign keys' => array(
       'aggregator_feed' => array(
@@ -325,6 +326,15 @@ function aggregator_update_7003() {
   db_add_index('aggregator_feed', 'url', array(array('url', 255)));
 }
 
+/**
+ * Add index on timestamp.
+ */
+function aggregator_update_7004() {
+  if (!db_index_exists('aggregator_item', 'timestamp')) {
+    db_add_index('aggregator_item', 'timestamp', array('timestamp'));
+  }
+}
+
 /**
  * @} End of "addtogroup updates-7.x-extra"
  */

+ 16 - 1
modules/aggregator/aggregator.test

@@ -288,6 +288,10 @@ EOF;
     return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_atom.xml';
   }
 
+  function getHtmlEntitiesSample() {
+    return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/aggregator_test_title_entities.xml';
+  }
+
   /**
    * Creates sample article nodes.
    *
@@ -931,7 +935,7 @@ class AggregatorRenderingTestCase extends AggregatorTestCase {
     // up.
     $feed->block = 0;
     aggregator_save_feed((array) $feed);
-    // It is nescessary to flush the cache after saving the number of items.
+    // It is necessary to flush the cache after saving the number of items.
     drupal_flush_all_caches();
     // Check that the block is no longer displayed.
     $this->drupalGet('node');
@@ -1016,4 +1020,15 @@ class FeedParserTestCase extends AggregatorTestCase {
     $this->assertText('Some text.');
     $this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', array(':link' => 'http://example.org/2003/12/13/atom03'))->fetchField(), 'Atom entry id element is parsed correctly.');
   }
+
+  /**
+   * Tests a feed that uses HTML entities in item titles.
+   */
+  function testHtmlEntitiesSample() {
+    $feed = $this->createFeed($this->getHtmlEntitiesSample());
+    aggregator_refresh($feed);
+    $this->drupalGet('aggregator/sources/' . $feed->fid);
+    $this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->title)));
+    $this->assertRaw("Quote&quot; Amp&amp;");
+  }
 }

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

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 14 - 0
modules/aggregator/tests/aggregator_test_title_entities.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="0.91">
+  <channel>
+    <title>Example with Entities</title>
+    <link>http://example.com</link>
+    <description>Example RSS Feed With HTML Entities in Title</description>
+    <language>en-us</language>
+    <item>
+      <title>Quote&quot; Amp&amp;</title>
+      <link>http://example.com/example-turns-one</link>
+      <description>Some text.</description>
+    </item>
+  </channel>
+</rss>

+ 17 - 13
modules/block/block.api.php

@@ -87,13 +87,13 @@
  *     and any value provided can be modified by a user on the block
  *     configuration screen.
  *   - pages: (optional) See 'visibility' above. A string that contains one or
- *     more page paths separated by '\n', '\r', or '\r\n' when 'visibility' is
- *     set to BLOCK_VISIBILITY_NOTLISTED or BLOCK_VISIBILITY_LISTED, or custom
- *     PHP code when 'visibility' is set to BLOCK_VISIBILITY_PHP. Paths may use
- *     '*' as a wildcard (matching any number of characters); '<front>'
- *     designates the site's front page. For BLOCK_VISIBILITY_PHP, the PHP
- *     code's return value should be TRUE if the block is to be made visible or
- *     FALSE if the block should not be visible.
+ *     more page paths separated by "\n", "\r", or "\r\n" when 'visibility' is
+ *     set to BLOCK_VISIBILITY_NOTLISTED or BLOCK_VISIBILITY_LISTED (example:
+ *     "<front>\nnode/1"), or custom PHP code when 'visibility' is set to
+ *     BLOCK_VISIBILITY_PHP. Paths may use '*' as a wildcard (matching any
+ *     number of characters); '<front>' designates the site's front page. For
+ *     BLOCK_VISIBILITY_PHP, the PHP code's return value should be TRUE if the
+ *     block is to be made visible or FALSE if the block should not be visible.
  *
  * For a detailed usage example, see block_example.module.
  *
@@ -200,11 +200,13 @@ function hook_block_save($delta = '', $edit = array()) {
  *   within the module, defined in hook_block_info().
  *
  * @return
- *   An array containing the following elements:
+ *   Either an empty array so the block will not be shown or an array containing
+ *   the following elements:
  *   - subject: The default localized title of the block. If the block does not
  *     have a default title, this should be set to NULL.
  *   - content: The content of the block's body. This may be a renderable array
- *     (preferable) or a string containing rendered HTML content.
+ *     (preferable) or a string containing rendered HTML content. If the content
+ *     is empty the block will not be shown.
  *
  * For a detailed usage example, see block_example.module.
  *
@@ -253,8 +255,9 @@ function hook_block_view($delta = '') {
  * specific block.
  *
  * @param $data
- *   An array of data, as returned from the hook_block_view() implementation of
- *   the module that defined the block:
+ *   The data as returned from the hook_block_view() implementation of the
+ *   module that defined the block. This could be an empty array or NULL value
+ *   (if the block is empty) or an array containing:
  *   - subject: The default localized title of the block.
  *   - content: Either a string or a renderable array representing the content
  *     of the block. You should check that the content is an array before trying
@@ -287,8 +290,9 @@ function hook_block_view_alter(&$data, $block) {
  * specific block, rather than implementing hook_block_view_alter().
  *
  * @param $data
- *   An array of data, as returned from the hook_block_view() implementation of
- *   the module that defined the block:
+ *   The data as returned from the hook_block_view() implementation of the
+ *   module that defined the block. This could be an empty array or NULL value
+ *   (if the block is empty) or an array containing:
  *   - subject: The localized title of the block.
  *   - content: Either a string or a renderable array representing the content
  *     of the block. You should check that the content is an array before trying

+ 3 - 3
modules/block/block.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = block.test
 configure = admin/structure/block
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 23 - 9
modules/block/block.module

@@ -401,23 +401,27 @@ function _block_rehash($theme = NULL) {
   }
   // Save the blocks defined in code for alter context.
   $code_blocks = $current_blocks;
-  $database_blocks = db_select('block', 'b')
+  $database_blocks = db_select('block', 'b', array('fetch' => PDO::FETCH_ASSOC))
     ->fields('b')
     ->condition($or)
     ->condition('theme', $theme)
     ->execute();
+  $original_database_blocks = array();
   foreach ($database_blocks as $block) {
-    // Preserve info which is not in the database.
-    $block->info = $current_blocks[$block->module][$block->delta]['info'];
+    $module = $block['module'];
+    $delta = $block['delta'];
+    $original_database_blocks[$module][$delta] = $block;
     // The cache mode can only by set from hook_block_info(), so that has
     // precedence over the database's value.
-    if (isset($current_blocks[$block->module][$block->delta]['cache'])) {
-      $block->cache = $current_blocks[$block->module][$block->delta]['cache'];
+    if (isset($current_blocks[$module][$delta]['cache'])) {
+      $block['cache'] = $current_blocks[$module][$delta]['cache'];
     }
+    // Preserve info which is not in the database.
+    $block['info'] = $current_blocks[$module][$delta]['info'];
     // Blocks stored in the database override the blocks defined in code.
-    $current_blocks[$block->module][$block->delta] = get_object_vars($block);
+    $current_blocks[$module][$delta] = $block;
     // Preserve this block.
-    $bids[$block->bid] = $block->bid;
+    $bids[$block['bid']] = $block['bid'];
   }
   drupal_alter('block_info', $current_blocks, $theme, $code_blocks);
   foreach ($current_blocks as $module => $module_blocks) {
@@ -456,7 +460,15 @@ function _block_rehash($theme = NULL) {
       else {
         $primary_keys = array();
       }
-      drupal_write_record('block', $block, $primary_keys);
+      // If the block is new or differs from the original database block, save
+      // it. To determine whether there was a change it is enough to examine
+      // the values for the keys in the original database record as that
+      // contained every database field.
+      if (!$primary_keys || array_diff_assoc($original_database_blocks[$module][$delta], $block)) {
+        drupal_write_record('block', $block, $primary_keys);
+        // Make it possible to test this.
+        $block['saved'] = TRUE;
+      }
       // Add to the list of blocks we return.
       $blocks[] = $block;
     }
@@ -880,9 +892,11 @@ function _block_render_blocks($region_blocks) {
       else {
         $array = module_invoke($block->module, 'block_view', $block->delta);
 
+        // Valid PHP function names cannot contain hyphens.
+        $delta = str_replace('-', '_', $block->delta);
         // Allow modules to modify the block before it is viewed, via either
         // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
-        drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block);
+        drupal_alter(array('block_view', "block_view_{$block->module}_{$delta}"), $array, $block);
 
         if (isset($cid)) {
           cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);

+ 121 - 1
modules/block/block.test

@@ -193,7 +193,7 @@ class BlockTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Test block visibility when using "pages" restriction but leaving 
+   * Test block visibility when using "pages" restriction but leaving
    * "pages" textarea empty
    */
   function testBlockVisibilityListedEmpty() {
@@ -752,6 +752,48 @@ class BlockTemplateSuggestionsUnitTest extends DrupalUnitTestCase {
   }
 }
 
+/**
+ * Tests for hook_block_view_MODULE_DELTA_alter().
+ */
+class BlockViewModuleDeltaAlterWebTest extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Block view module delta alter',
+      'description' => 'Test the hook_block_view_MODULE_DELTA_alter() hook.',
+      'group' => 'Block',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp(array('block_test'));
+  }
+
+  /**
+   * Tests that the alter hook is called, even if the delta contains a hyphen.
+   */
+  public function testBlockViewModuleDeltaAlter() {
+    $block = new stdClass;
+    $block->module = 'block_test';
+    $block->delta = 'test_underscore';
+    $block->title = '';
+    $render_array = _block_render_blocks(array('region' => $block));
+    $render = array_pop($render_array);
+    $test_underscore = $render->content['#markup'];
+    $this->assertEqual($test_underscore, 'hook_block_view_MODULE_DELTA_alter', 'Found expected altered block content for delta with underscore');
+
+    $block = new stdClass;
+    $block->module = 'block_test';
+    $block->delta = 'test-hyphen';
+    $block->title = '';
+    $render_array = _block_render_blocks(array('region' => $block));
+    $render = array_pop($render_array);
+    $test_hyphen = $render->content['#markup'];
+    $this->assertEqual($test_hyphen, 'hook_block_view_MODULE_DELTA_alter', 'Hyphens (-) in block delta were replaced by underscore (_)');
+  }
+
+}
+
 /**
  * Tests that hidden regions do not inherit blocks when a theme is enabled.
  */
@@ -857,3 +899,81 @@ class BlockInvalidRegionTestCase extends DrupalWebTestCase {
     $this->assertNoRaw($warning_message, 'Disabled block in the invalid region will not trigger the warning.');
   }
 }
+
+/**
+ * Tests that block rehashing works correctly.
+ */
+class BlockHashTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Block rehash',
+      'description' => 'Checks _block_rehash() functionality.',
+      'group' => 'Block',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('block'));
+  }
+
+  /**
+   * Tests that block rehashing does not write to the database too often.
+   */
+  function testBlockRehash() {
+    // No hook_block_info_alter(), no save.
+    $this->doRehash();
+    module_enable(array('block_test'), FALSE);
+    // Save the new blocks, check that the new blocks exist by checking weight.
+    _block_rehash();
+    $this->assertWeight(0);
+    // Now hook_block_info_alter() exists but no blocks are saved on a second
+    // rehash.
+    $this->doRehash();
+    $this->assertWeight(0);
+    // Now hook_block_info_alter() exists and is changing one block which
+    // should be saved.
+    $GLOBALS['conf']['block_test_info_alter'] = 1;
+    $this->doRehash(TRUE);
+    $this->assertWeight(10000);
+    // Now hook_block_info_alter() exists but already changed the block's
+    // weight before, so it should not be saved again.
+    $this->doRehash();
+    $this->assertWeight(10000);
+  }
+
+  /**
+   * Performs a block rehash and checks several related assertions.
+   *
+   * @param $alter_active
+   *   Set to TRUE if the block_test module's hook_block_info_alter()
+   *   implementation is expected to make a change that results in an existing
+   *   block needing to be resaved to the database. Defaults to FALSE.
+   */
+  function doRehash($alter_active = FALSE) {
+    $saves = 0;
+    foreach (_block_rehash() as $block) {
+      $module = $block['module'];
+      $delta = $block['delta'];
+      if ($alter_active && $module == 'block_test' && $delta == 'test_html_id') {
+        $this->assertFalse(empty($block['saved']), "$module $delta saved");
+        $saves++;
+      }
+      else {
+        $this->assertTrue(empty($block['saved']), "$module $delta not saved");
+      }
+    }
+    $this->assertEqual($alter_active, $saves);
+  }
+
+  /**
+   * Asserts that the block_test module's block has a given weight.
+   *
+   * @param $weight
+   *   The expected weight.
+   */
+  function assertWeight($weight) {
+    $db_weight = db_query('SELECT weight FROM {block} WHERE module = :module AND delta = :delta', array(':module' => 'block_test', ':delta' => 'test_html_id'))->fetchField();
+    // By casting to string the assert fails on FALSE.
+    $this->assertIdentical((string) $db_weight, (string) $weight);
+  }
+}

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

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 31 - 0
modules/block/tests/block_test.module

@@ -22,6 +22,14 @@ function block_test_block_info() {
     'cache' => variable_get('block_test_caching', DRUPAL_CACHE_PER_ROLE),
   );
 
+  $blocks['test_underscore'] = array(
+    'info' => t('Test underscore'),
+  );
+
+  $blocks['test-hyphen'] = array(
+    'info' => t('Test hyphen'),
+  );
+
   $blocks['test_html_id'] = array(
     'info' => t('Test block html id'),
   );
@@ -34,3 +42,26 @@ function block_test_block_info() {
 function block_test_block_view($delta = 0) {
   return array('content' => variable_get('block_test_content', ''));
 }
+
+/**
+ * Implements hook_block_view_MODULE_DELTA_alter().
+ */
+function block_test_block_view_block_test_test_underscore_alter(&$data, $block) {
+  $data['content'] = 'hook_block_view_MODULE_DELTA_alter';
+}
+
+/**
+ * Implements hook_block_view_MODULE_DELTA_alter().
+ */
+function block_test_block_view_block_test_test_hyphen_alter(&$data, $block) {
+  $data['content'] = 'hook_block_view_MODULE_DELTA_alter';
+}
+
+/**
+ * Implements hook_block_info_alter().
+ */
+function block_test_block_info_alter(&$blocks) {
+  if (variable_get('block_test_info_alter')) {
+    $blocks['block_test']['test_html_id']['weight'] = 10000;
+  }
+}

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

@@ -13,8 +13,8 @@ regions[footer] = Footer
 regions[highlighted] = Highlighted
 regions[help] = Help
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 3 - 3
modules/blog/blog.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 files[] = blog.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 20 - 20
modules/blog/blog.test

@@ -42,8 +42,8 @@ class BlogTestCase extends DrupalWebTestCase {
 
     $this->drupalGet('blog/' . $this->big_user->uid);
     $this->assertResponse(200);
-    $this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user))) . ' | Drupal', t('Blog title was displayed'));
-    $this->assertText(t('You are not allowed to post a new blog entry.'), t('No new entries can be posted without the right permission'));
+    $this->assertTitle(t("@name's blog", array('@name' => format_username($this->big_user))) . ' | Drupal', 'Blog title was displayed');
+    $this->assertText(t('You are not allowed to post a new blog entry.'), 'No new entries can be posted without the right permission');
   }
 
   /**
@@ -54,8 +54,8 @@ class BlogTestCase extends DrupalWebTestCase {
 
     $this->drupalGet('blog/' . $this->own_user->uid);
     $this->assertResponse(200);
-    $this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user))) . ' | Drupal', t('Blog title was displayed'));
-    $this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), t('Users blog displayed with no entries'));
+    $this->assertTitle(t("@name's blog", array('@name' => format_username($this->own_user))) . ' | Drupal', 'Blog title was displayed');
+    $this->assertText(t('@author has not created any blog entries.', array('@author' => format_username($this->own_user))), 'Users blog displayed with no entries');
   }
 
   /**
@@ -73,7 +73,7 @@ class BlogTestCase extends DrupalWebTestCase {
     $edit = array();
     $edit['blog_block_count'] = 5;
     $this->drupalPost('admin/structure/block/manage/blog/recent/configure', $edit, t('Save block'));
-    $this->assertEqual(variable_get('blog_block_count', 10), 5, t('Number of recent blog posts changed.'));
+    $this->assertEqual(variable_get('blog_block_count', 10), 5, 'Number of recent blog posts changed.');
 
     // Do basic tests for each user.
     $this->doBasicTests($this->any_user, TRUE);
@@ -132,31 +132,31 @@ class BlogTestCase extends DrupalWebTestCase {
     $this->drupalGet('admin/help/blog');
     $this->assertResponse($response2);
     if ($response2 == 200) {
-      $this->assertTitle(t('Blog | Drupal'), t('Blog help node was displayed'));
-      $this->assertText(t('Blog'), t('Blog help node was displayed'));
+      $this->assertTitle(t('Blog | Drupal'), 'Blog help node was displayed');
+      $this->assertText(t('Blog'), 'Blog help node was displayed');
     }
 
     // Verify the blog block was displayed.
     $this->drupalGet('');
     $this->assertResponse(200);
-    $this->assertText(t('Recent blog posts'), t('Blog block was displayed'));
+    $this->assertText(t('Recent blog posts'), 'Blog block was displayed');
 
     // View blog node.
     $this->drupalGet('node/' . $node->nid);
     $this->assertResponse(200);
-    $this->assertTitle($node->title . ' | Drupal', t('Blog node was displayed'));
+    $this->assertTitle($node->title . ' | Drupal', 'Blog node was displayed');
     $breadcrumb = array(
       l(t('Home'), NULL),
       l(t('Blogs'), 'blog'),
       l(t("!name's blog", array('!name' => format_username($node_user))), 'blog/' . $node_user->uid),
     );
-    $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), t('Breadcrumbs were displayed'));
+    $this->assertRaw(theme('breadcrumb', array('breadcrumb' => $breadcrumb)), 'Breadcrumbs were displayed');
 
     // View blog edit node.
     $this->drupalGet('node/' . $node->nid . '/edit');
     $this->assertResponse($response);
     if ($response == 200) {
-      $this->assertTitle('Edit Blog entry ' . $node->title . ' | Drupal', t('Blog edit node was displayed'));
+      $this->assertTitle('Edit Blog entry ' . $node->title . ' | Drupal', 'Blog edit node was displayed');
     }
 
     if ($response == 200) {
@@ -166,12 +166,12 @@ class BlogTestCase extends DrupalWebTestCase {
       $edit["title"] = 'node/' . $node->nid;
       $edit["body[$langcode][0][value]"] = $this->randomName(256);
       $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
-      $this->assertRaw(t('Blog entry %title has been updated.', array('%title' => $edit["title"])), t('Blog node was edited'));
+      $this->assertRaw(t('Blog entry %title has been updated.', array('%title' => $edit["title"])), 'Blog node was edited');
 
       // Delete blog node.
       $this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
       $this->assertResponse($response);
-      $this->assertRaw(t('Blog entry %title has been deleted.', array('%title' => $edit["title"])), t('Blog node was deleted'));
+      $this->assertRaw(t('Blog entry %title has been deleted.', array('%title' => $edit["title"])), 'Blog node was deleted');
     }
   }
 
@@ -185,29 +185,29 @@ class BlogTestCase extends DrupalWebTestCase {
     // Confirm blog entries link exists on the user page.
     $this->drupalGet('user/' . $user->uid);
     $this->assertResponse(200);
-    $this->assertText(t('View recent blog entries'), t('View recent blog entries link was displayed'));
+    $this->assertText(t('View recent blog entries'), 'View recent blog entries link was displayed');
 
     // Confirm the recent blog entries link goes to the user's blog page.
     $this->clickLink('View recent blog entries');
-    $this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('View recent blog entries link target was correct'));
+    $this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), 'View recent blog entries link target was correct');
 
     // Confirm a blog page was displayed.
     $this->drupalGet('blog');
     $this->assertResponse(200);
-    $this->assertTitle('Blogs | Drupal', t('Blog page was displayed'));
-    $this->assertText(t('Home'), t('Breadcrumbs were displayed'));
+    $this->assertTitle('Blogs | Drupal', 'Blog page was displayed');
+    $this->assertText(t('Home'), 'Breadcrumbs were displayed');
     $this->assertLink(t('Create new blog entry'));
 
     // Confirm a blog page was displayed per user.
     $this->drupalGet('blog/' . $user->uid);
-    $this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), t('User blog node was displayed'));
+    $this->assertTitle(t("@name's blog | Drupal", array('@name' => format_username($user))), 'User blog node was displayed');
 
     // Confirm a blog feed was displayed.
     $this->drupalGet('blog/feed');
-    $this->assertTitle(t('Drupal blogs'), t('Blog feed was displayed'));
+    $this->assertTitle(t('Drupal blogs'), 'Blog feed was displayed');
 
     // Confirm a blog feed was displayed per user.
     $this->drupalGet('blog/' . $user->uid . '/feed');
-    $this->assertTitle(t("@name's blog", array('@name' => format_username($user))), t('User blog feed was displayed'));
+    $this->assertTitle(t("@name's blog", array('@name' => format_username($user))), 'User blog feed was displayed');
   }
 }

+ 3 - 3
modules/book/book.info

@@ -7,8 +7,8 @@ files[] = book.test
 configure = admin/content/book/settings
 stylesheets[all][] = book.css
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 3 - 3
modules/color/color.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 files[] = color.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 48 - 1
modules/color/color.module

@@ -240,6 +240,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) {
       $form['palette'][$name] = array(
         '#type' => 'textfield',
         '#title' => check_plain($names[$name]),
+        '#value_callback' => 'color_palette_color_value',
         '#default_value' => $value,
         '#size' => 8,
       );
@@ -294,6 +295,52 @@ function theme_color_scheme_form($variables) {
   return $output;
 }
 
+/**
+ * Determines the value for a palette color field.
+ *
+ * @param $element
+ *   The form element whose value is being populated.
+ * @param $input
+ *   The incoming input to populate the form element. If this is FALSE,
+ *   the element's default value should be returned.
+ * @param $form_state
+ *   A keyed array containing the current state of the form.
+ *
+ * @return
+ *   The data that will appear in the $form_state['values'] collection for this
+ *   element. Return nothing to use the default.
+ */
+function color_palette_color_value($element, $input = FALSE, $form_state = array()) {
+  // If we suspect a possible cross-site request forgery attack, only accept
+  // hexadecimal CSS color strings from user input, to avoid problems when this
+  // value is used in the JavaScript preview.
+  if ($input !== FALSE) {
+    // Start with the provided value for this textfield, and validate that if
+    // necessary, falling back on the default value.
+    $value = form_type_textfield_value($element, $input, $form_state);
+    if (!$value || !isset($form_state['complete form']['#token']) || color_valid_hexadecimal_string($value) || drupal_valid_token($form_state['values']['form_token'], $form_state['complete form']['#token'])) {
+      return $value;
+    }
+    else {
+      return $element['#default_value'];
+    }
+  }
+}
+
+/**
+ * Determines if a hexadecimal CSS color string is valid.
+ *
+ * @param $color
+ *   The string to check.
+ *
+ * @return
+ *   TRUE if the string is a valid hexadecimal CSS color string, or FALSE if it
+ *   isn't.
+ */
+function color_valid_hexadecimal_string($color) {
+  return preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color);
+}
+
 /**
  * Form validation handler for color_scheme_form().
  *
@@ -302,7 +349,7 @@ function theme_color_scheme_form($variables) {
 function color_scheme_form_validate($form, &$form_state) {
   // Only accept hexadecimal CSS color strings to avoid XSS upon use.
   foreach ($form_state['values']['palette'] as $key => $color) {
-    if (!preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color)) {
+    if (!color_valid_hexadecimal_string($color)) {
       form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title'])));
     }
   }

+ 3 - 3
modules/comment/comment.info

@@ -9,8 +9,8 @@ files[] = comment.test
 configure = admin/content/comment
 stylesheets[all][] = comment.css
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 2 - 2
modules/comment/comment.module

@@ -490,7 +490,7 @@ function comment_permalink($cid) {
     // Return the node view, this will show the correct comment in context.
     return menu_execute_active_handler('node/' . $node->nid, FALSE);
   }
-  drupal_not_found();
+  return MENU_NOT_FOUND;
 }
 
 /**
@@ -2304,7 +2304,7 @@ function template_preprocess_comment(&$variables) {
   $variables['signature'] = $comment->signature;
 
   $uri = entity_uri('comment', $comment);
-  $uri['options'] += array('attributes' => array('class' => 'permalink', 'rel' => 'bookmark'));
+  $uri['options'] += array('attributes' => array('class' => array('permalink'), 'rel' => 'bookmark'));
 
   $variables['title']     = l($comment->subject, $uri['path'], $uri['options']);
   $variables['permalink'] = l(t('Permalink'), $uri['path'], $uri['options']);

+ 16 - 16
modules/comment/comment.test

@@ -155,7 +155,7 @@ class CommentHelperCase extends DrupalWebTestCase {
         $mode_text = 'required';
         break;
     }
-    $this->setCommentSettings('comment_preview', $mode, 'Comment preview ' . $mode_text . '.');
+    $this->setCommentSettings('comment_preview', $mode, format_string('Comment preview @mode_text.', array('@mode_text' => $mode_text)));
   }
 
   /**
@@ -175,7 +175,7 @@ class CommentHelperCase extends DrupalWebTestCase {
    *   Anonymous level.
    */
   function setCommentAnonymous($level) {
-    $this->setCommentSettings('comment_anonymous', $level, 'Anonymous commenting set to level ' . $level . '.');
+    $this->setCommentSettings('comment_anonymous', $level, format_string('Anonymous commenting set to level @level.', array('@level' => $level)));
   }
 
   /**
@@ -185,7 +185,7 @@ class CommentHelperCase extends DrupalWebTestCase {
    *   Comments per page value.
    */
   function setCommentsPerPage($number) {
-    $this->setCommentSettings('comment_default_per_page', $number, 'Number of comments per page set to ' . $number . '.');
+    $this->setCommentSettings('comment_default_per_page', $number, format_string('Number of comments per page set to @number.', array('@number' => $number)));
   }
 
   /**
@@ -201,7 +201,7 @@ class CommentHelperCase extends DrupalWebTestCase {
   function setCommentSettings($name, $value, $message) {
     variable_set($name . '_article', $value);
     // Display status message.
-    $this->assertTrue(TRUE, $message);
+    $this->pass($message);
   }
 
   /**
@@ -273,7 +273,7 @@ class CommentInterfaceTest extends CommentHelperCase {
     $this->setCommentPreview(DRUPAL_DISABLED);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(FALSE);
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
     $this->drupalLogout();
 
     // Post comment #1 without subject or preview.
@@ -583,7 +583,7 @@ class CommentInterfaceTest extends CommentHelperCase {
     $this->setCommentPreview(DRUPAL_DISABLED);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(FALSE);
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
     $this->drupalLogout();
 
     // Creates a second user to post comments.
@@ -954,7 +954,7 @@ class CommentPreviewTest extends CommentHelperCase {
     $this->setCommentPreview(DRUPAL_OPTIONAL);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(TRUE);
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
     $this->drupalLogout();
 
     // Login as web user and add a signature and a user picture.
@@ -1000,7 +1000,7 @@ class CommentPreviewTest extends CommentHelperCase {
     $this->setCommentPreview(DRUPAL_OPTIONAL);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(TRUE);
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
 
     $edit = array();
     $edit['subject'] = $this->randomName(8);
@@ -1238,7 +1238,7 @@ class CommentPagerTest extends CommentHelperCase {
     $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
     $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
 
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
 
     // Set comments to one per page so that we are able to test paging without
     // needing to insert large numbers of comments.
@@ -1279,7 +1279,7 @@ class CommentPagerTest extends CommentHelperCase {
     // If we switch to threaded mode, the replies on the oldest comment
     // should be bumped to the first page and comment 6 should be bumped
     // to the second page.
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Switched to threaded mode.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
     $this->drupalGet('node/' . $node->nid, array('query' => array('page' => 0)));
     $this->assertTrue($this->commentExists($reply, TRUE), 'In threaded mode, reply appears on page 1.');
     $this->assertFalse($this->commentExists($comments[1]), 'In threaded mode, comment 2 has been bumped off of page 1.');
@@ -1339,7 +1339,7 @@ class CommentPagerTest extends CommentHelperCase {
     // - 2
     //   - 5
 
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
 
     $expected_order = array(
       0,
@@ -1353,7 +1353,7 @@ class CommentPagerTest extends CommentHelperCase {
     $this->drupalGet('node/' . $node->nid);
     $this->assertCommentOrder($comments, $expected_order);
 
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Switched to threaded mode.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
 
     $expected_order = array(
       0,
@@ -1435,7 +1435,7 @@ class CommentPagerTest extends CommentHelperCase {
     // - 2
     //   - 5
 
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
 
     $expected_pages = array(
       1 => 5, // Page of comment 5
@@ -1453,7 +1453,7 @@ class CommentPagerTest extends CommentHelperCase {
       $this->assertIdentical($expected_page, $returned_page, format_string('Flat mode, @new replies: expected page @expected, returned page @returned.', array('@new' => $new_replies, '@expected' => $expected_page, '@returned' => $returned_page)));
     }
 
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Switched to threaded mode.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
 
     $expected_pages = array(
       1 => 5, // Page of comment 5
@@ -1509,7 +1509,7 @@ class CommentNodeAccessTest extends CommentHelperCase {
     $this->setCommentPreview(DRUPAL_DISABLED);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(TRUE);
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
     $this->drupalLogout();
 
     // Post comment.
@@ -2126,7 +2126,7 @@ class CommentThreadingTestCase extends CommentHelperCase {
     $this->setCommentPreview(DRUPAL_DISABLED);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(TRUE);
-    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
+    $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
     $this->drupalLogout();
 
     // Create a node.

+ 3 - 3
modules/contact/contact.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = contact.test
 configure = admin/structure/contact
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 3 - 3
modules/contextual/contextual.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 files[] = contextual.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -32,7 +32,7 @@ function hook_dashboard_regions() {
  *   An array containing all dashboard regions, in the format provided by
  *   hook_dashboard_regions().
  */
-function hook_dashboard_regions_alter($regions) {
+function hook_dashboard_regions_alter(&$regions) {
   // Remove the sidebar region defined by the core dashboard module.
   unset($regions['dashboard_sidebar']);
 }

+ 3 - 3
modules/dashboard/dashboard.info

@@ -7,8 +7,8 @@ files[] = dashboard.test
 dependencies[] = block
 configure = admin/dashboard/customize
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 19 - 19
modules/dashboard/dashboard.test

@@ -49,15 +49,15 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
 
     // Ensure admin access.
     $this->drupalGet('admin/dashboard');
-    $this->assertResponse(200, t('Admin has access to the dashboard.'));
-    $this->assertRaw($custom_block['title'], t('Admin has access to a dashboard block.'));
+    $this->assertResponse(200, 'Admin has access to the dashboard.');
+    $this->assertRaw($custom_block['title'], 'Admin has access to a dashboard block.');
 
     // Ensure non-admin access is denied.
     $normal_user = $this->drupalCreateUser();
     $this->drupalLogin($normal_user);
     $this->drupalGet('admin/dashboard');
-    $this->assertResponse(403, t('Non-admin has no access to the dashboard.'));
-    $this->assertNoText($custom_block['title'], t('Non-admin has no access to a dashboard block.'));
+    $this->assertResponse(403, 'Non-admin has no access to the dashboard.');
+    $this->assertNoText($custom_block['title'], 'Non-admin has no access to a dashboard block.');
   }
 
   /**
@@ -70,7 +70,7 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
     $this->drupalGet('admin/dashboard/configure');
     foreach ($dashboard_regions as $region => $description) {
       $elements = $this->xpath('//option[@value=:region]', array(':region' => $region));
-      $this->assertTrue(!empty($elements), t('%region is an available choice on the dashboard block configuration page.', array('%region' => $region)));
+      $this->assertTrue(!empty($elements), format_string('%region is an available choice on the dashboard block configuration page.', array('%region' => $region)));
     }
 
     // Ensure blocks cannot be placed in dashboard regions on the standard
@@ -78,7 +78,7 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
     $this->drupalGet('admin/structure/block');
     foreach ($dashboard_regions as $region => $description) {
       $elements = $this->xpath('//option[@value=:region]', array(':region' => $region));
-      $this->assertTrue(empty($elements), t('%region is not an available choice on the block configuration page.', array('%region' => $region)));
+      $this->assertTrue(empty($elements), format_string('%region is not an available choice on the block configuration page.', array('%region' => $region)));
     }
   }
 
@@ -94,24 +94,24 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
     $custom_block['regions[stark]'] = 'dashboard_main';
     $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
     $this->drupalGet('admin/dashboard');
-    $this->assertRaw($custom_block['title'], t('Block appears on the dashboard.'));
+    $this->assertRaw($custom_block['title'], 'Block appears on the dashboard.');
 
     $edit = array();
     $edit['modules[Core][dashboard][enable]'] = FALSE;
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
-    $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
-    $this->assertNoRaw('assigned to the invalid region', t('Dashboard blocks gracefully disabled.'));
+    $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
+    $this->assertNoRaw('assigned to the invalid region', 'Dashboard blocks gracefully disabled.');
     module_list(TRUE);
-    $this->assertFalse(module_exists('dashboard'), t('Dashboard disabled.'));
+    $this->assertFalse(module_exists('dashboard'), 'Dashboard disabled.');
 
     $edit['modules[Core][dashboard][enable]'] = 'dashboard';
     $this->drupalPost('admin/modules', $edit, t('Save configuration'));
-    $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.'));
+    $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.');
     module_list(TRUE);
-    $this->assertTrue(module_exists('dashboard'), t('Dashboard enabled.'));
+    $this->assertTrue(module_exists('dashboard'), 'Dashboard enabled.');
 
     $this->drupalGet('admin/dashboard');
-    $this->assertRaw($custom_block['title'], t('Block still appears on the dashboard.'));
+    $this->assertRaw($custom_block['title'], 'Block still appears on the dashboard.');
   }
 
   /**
@@ -121,21 +121,21 @@ class DashboardBlocksTestCase extends DrupalWebTestCase {
     // Test "Recent comments", which should be available (defined as
     // "administrative") but not enabled.
     $this->drupalGet('admin/dashboard');
-    $this->assertNoText(t('Recent comments'), t('"Recent comments" not on dashboard.'));
+    $this->assertNoText(t('Recent comments'), '"Recent comments" not on dashboard.');
     $this->drupalGet('admin/dashboard/drawer');
-    $this->assertText(t('Recent comments'), t('Drawer of disabled blocks includes a block defined as "administrative".'));
-    $this->assertNoText(t('Syndicate'), t('Drawer of disabled blocks excludes a block not defined as "administrative".'));
+    $this->assertText(t('Recent comments'), 'Drawer of disabled blocks includes a block defined as "administrative".');
+    $this->assertNoText(t('Syndicate'), 'Drawer of disabled blocks excludes a block not defined as "administrative".');
     $this->drupalGet('admin/dashboard/configure');
     $elements = $this->xpath('//select[@id=:id]//option[@selected="selected"]', array(':id' => 'edit-blocks-comment-recent-region'));
-    $this->assertTrue($elements[0]['value'] == 'dashboard_inactive', t('A block defined as "administrative" defaults to dashboard_inactive.'));
+    $this->assertTrue($elements[0]['value'] == 'dashboard_inactive', 'A block defined as "administrative" defaults to dashboard_inactive.');
 
     // Now enable the block on the dashboard.
     $values = array();
     $values['blocks[comment_recent][region]'] = 'dashboard_main';
     $this->drupalPost('admin/dashboard/configure', $values, t('Save blocks'));
     $this->drupalGet('admin/dashboard');
-    $this->assertText(t('Recent comments'), t('"Recent comments" was placed on dashboard.'));
+    $this->assertText(t('Recent comments'), '"Recent comments" was placed on dashboard.');
     $this->drupalGet('admin/dashboard/drawer');
-    $this->assertNoText(t('Recent comments'), t('Drawer of disabled blocks excludes enabled blocks.'));
+    $this->assertNoText(t('Recent comments'), 'Drawer of disabled blocks excludes enabled blocks.');
   }
 }

+ 3 - 3
modules/dblog/dblog.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 files[] = dblog.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 1 - 1
modules/dblog/dblog.test

@@ -11,7 +11,7 @@
 class DBLogTestCase extends DrupalWebTestCase {
 
   /**
-   * A user with some relevent administrative permissions.
+   * A user with some relevant administrative permissions.
    *
    * @var object
    */

+ 60 - 9
modules/field/field.api.php

@@ -1,4 +1,8 @@
 <?php
+/**
+ * @file
+ * Hooks provided by the Field module.
+ */
 
 /**
  * @addtogroup hooks
@@ -37,6 +41,8 @@
  *   - delete: (optional) String containing markup (normally a link) used as the
  *     element's 'delete' operation in the administration interface. Only for
  *     'form' context.
+ *
+ * @ingroup field_types
  */
 function hook_field_extra_fields() {
   $extra['node']['poll'] = array(
@@ -76,6 +82,8 @@ function hook_field_extra_fields() {
  *   The associative array of 'pseudo-field' components.
  *
  * @see hook_field_extra_fields()
+ *
+ * @ingroup field_types
  */
 function hook_field_extra_fields_alter(&$info) {
   // Force node title to always be at the top of the list by default.
@@ -113,6 +121,9 @@ function hook_field_extra_fields_alter(&$info) {
 /**
  * Define Field API field types.
  *
+ * Along with this hook, you also need to implement other hooks. See
+ * @link field_types Field Types API @endlink for more information.
+ *
  * @return
  *   An array whose keys are field type names and whose values are arrays
  *   describing the field type, with the following key/value pairs:
@@ -199,8 +210,11 @@ function hook_field_info_alter(&$info) {
 /**
  * Define the Field API schema for a field structure.
  *
- * This hook MUST be defined in .install for it to be detected during
- * installation and upgrade.
+ * This is invoked when a field is created, in order to obtain the database
+ * schema from the module that defines the field's type.
+ *
+ * This hook must be defined in the module's .install file for it to be detected
+ * during installation and upgrade.
  *
  * @param $field
  *   A field structure.
@@ -650,6 +664,8 @@ function hook_field_delete_revision($entity_type, $entity, $field, $instance, $l
  *   The source entity from which field values are being copied.
  * @param $source_langcode
  *   The source language from which field values are being copied.
+ *
+ * @ingroup field_language
  */
 function hook_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
   // If the translating user is not permitted to use the assigned text format,
@@ -1244,7 +1260,7 @@ function hook_field_formatter_view($entity_type, $entity, $field, $instance, $la
  */
 
 /**
- * @ingroup field_attach
+ * @addtogroup field_attach
  * @{
  */
 
@@ -1306,6 +1322,13 @@ function hook_field_attach_load($entity_type, $entities, $age, $options) {
  * This hook is invoked after the field module has performed the operation.
  *
  * See field_attach_validate() for details and arguments.
+ *
+ * @param $entity_type
+ *   The type of $entity; e.g., 'node' or 'user'.
+ * @param $entity
+ *   The entity with fields to validate.
+ * @param array $errors
+ *   An associative array of errors keyed by field_name, language, delta.
  */
 function hook_field_attach_validate($entity_type, $entity, &$errors) {
   // @todo Needs function body.
@@ -1510,6 +1533,8 @@ function hook_field_attach_prepare_translation_alter(&$entity, $context) {
  *   - entity_type: The type of the entity to be displayed.
  *   - entity: The entity with fields to render.
  *   - langcode: The language code $entity has to be displayed in.
+ *
+ * @ingroup field_language
  */
 function hook_field_language_alter(&$display_language, $context) {
   // Do not apply core language fallback rules if they are disabled or if Locale
@@ -1531,6 +1556,8 @@ function hook_field_language_alter(&$display_language, $context) {
  *   An associative array containing:
  *   - entity_type: The type of the entity the field is attached to.
  *   - field: A field data structure.
+ *
+ * @ingroup field_language
  */
 function hook_field_available_languages_alter(&$languages, $context) {
   // Add an unavailable language.
@@ -1581,7 +1608,7 @@ function hook_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new)
  * @param $entity_type
  *   The type of entity; for example, 'node' or 'user'.
  * @param $bundle
- *   The bundle that was just deleted.
+ *   The name of the bundle that was just deleted.
  * @param $instances
  *   An array of all instances that existed for the bundle before it was
  *   deleted.
@@ -1596,7 +1623,7 @@ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) {
 }
 
 /**
- * @} End of "defgroup field_attach".
+ * @} End of "addtogroup field_attach".
  */
 
 /**
@@ -2256,6 +2283,10 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
   }
 }
 
+/**
+ * @} End of "addtogroup field_storage
+ */
+
 /**
  * Returns the maximum weight for the entity components handled by the module.
  *
@@ -2269,9 +2300,12 @@ function hook_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
  * @param $context
  *   The context for which the maximum weight is requested. Either 'form', or
  *   the name of a view mode.
+ *
  * @return
  *   The maximum weight of the entity's components, or NULL if no components
  *   were found.
+ *
+ * @ingroup field_info
  */
 function hook_field_info_max_weight($entity_type, $bundle, $context) {
   $weights = array();
@@ -2283,6 +2317,11 @@ function hook_field_info_max_weight($entity_type, $bundle, $context) {
   return $weights ? max($weights) : NULL;
 }
 
+/**
+ * @addtogroup field_types
+ * @{
+ */
+
 /**
  * Alters the display settings of a field before it gets displayed.
  *
@@ -2349,6 +2388,10 @@ function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
   }
 }
 
+/**
+ * @} End of "addtogroup field_types
+ */
+
 /**
  * Alters the display settings of pseudo-fields before an entity is displayed.
  *
@@ -2364,6 +2407,8 @@ function hook_field_display_ENTITY_TYPE_alter(&$display, $context) {
  *   - entity_type: The entity type; e.g., 'node' or 'user'.
  *   - bundle: The bundle name.
  *   - view_mode: The view mode, e.g. 'full', 'teaser'...
+ *
+ * @ingroup field_types
  */
 function hook_field_extra_fields_display_alter(&$displays, $context) {
   if ($context['entity_type'] == 'taxonomy_term' && $context['view_mode'] == 'full') {
@@ -2393,6 +2438,8 @@ function hook_field_extra_fields_display_alter(&$displays, $context) {
  *   - instance: The instance of the field.
  *
  * @see hook_field_widget_properties_alter()
+ *
+ * @ingroup field_widget
  */
 function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
   // Change a widget's type according to the time of day.
@@ -2403,10 +2450,6 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
   }
 }
 
-/**
- * @} End of "addtogroup field_storage".
- */
-
 /**
  * @addtogroup field_crud
  * @{
@@ -2602,6 +2645,8 @@ function hook_field_purge_instance($instance) {
  *
  * @param $field
  *   The field being purged.
+ *
+ * @ingroup field_storage
  */
 function hook_field_storage_purge_field($field) {
   $table_name = _field_sql_storage_tablename($field);
@@ -2619,6 +2664,8 @@ function hook_field_storage_purge_field($field) {
  *
  * @param $instance
  *   The instance being purged.
+ *
+ * @ingroup field_storage
  */
 function hook_field_storage_purge_field_instance($instance) {
   db_delete('my_module_field_instance_info')
@@ -2640,6 +2687,8 @@ function hook_field_storage_purge_field_instance($instance) {
  *   The (possibly deleted) field whose data is being purged.
  * @param $instance
  *   The deleted field instance whose data is being purged.
+ *
+ * @ingroup field_storage
  */
 function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
   list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
@@ -2679,6 +2728,8 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
  *
  * @return
  *   TRUE if the operation is allowed, and FALSE if the operation is denied.
+ *
+ * @ingroup field_types
  */
 function hook_field_access($op, $field, $entity_type, $entity, $account) {
   if ($field['field_name'] == 'field_of_interest' && $op == 'edit') {

+ 6 - 0
modules/field/field.attach.inc

@@ -976,6 +976,12 @@ function field_attach_insert($entity_type, $entity) {
 /**
  * Save field data for an existing entity.
  *
+ * When calling this function outside an entity save operation be sure to
+ * clear caches for the entity:
+ * @code
+ * entity_get_controller($entity_type)->resetCache(array($entity_id))
+ * @endcode
+ *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
  * @param $entity

+ 3 - 3
modules/field/field.info

@@ -11,8 +11,8 @@ dependencies[] = field_sql_storage
 required = TRUE
 stylesheets[all][] = theme/field.css
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 2 - 2
modules/field/field.module

@@ -819,9 +819,9 @@ function field_view_value($entity_type, $entity, $field_name, $item, $display =
  *
  * This function can be used by third-party modules that need to output an
  * isolated field.
- * - Do not use inside node (or other entities) templates, use
+ * - Do not use inside node (or any other entity) templates; use
  *   render($content[FIELD_NAME]) instead.
- * - Do not use to display all fields in an entity, use
+ * - Do not use to display all fields in an entity; use
  *   field_attach_prepare_view() and field_attach_view() instead.
  * - The field_view_value() function can be used to output a single formatted
  *   field value, without label or wrapping field markup.

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

@@ -7,8 +7,8 @@ dependencies[] = field
 files[] = field_sql_storage.test
 required = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -7,8 +7,8 @@ dependencies[] = field
 dependencies[] = options
 files[] = tests/list.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -5,8 +5,8 @@ package = Testing
 version = VERSION
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 files[] = number.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 files[] = options.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -359,7 +359,7 @@ class OptionsWidgetsTestCase extends FieldTestCase {
 
     // Test the 'None' option.
 
-    // Check that the 'none' option has no efect if actual options are selected
+    // Check that the 'none' option has no effect if actual options are selected
     // as well.
     $edit = array("card_2[$langcode][]" => array('_none' => '_none', 0 => 0));
     $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));

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

@@ -7,8 +7,8 @@ dependencies[] = field
 files[] = text.test
 required = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

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

@@ -6,8 +6,8 @@ files[] = field_test.entity.inc
 version = VERSION
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 2 - 2
modules/field/tests/field_test.install

@@ -60,7 +60,7 @@ function field_test_schema() {
     'description' => 'The base table for test entities with a bundle key.',
     'fields' => array(
       'ftid' => array(
-        'description' => 'The primary indentifier for a test_entity_bundle_key.',
+        'description' => 'The primary identifier for a test_entity_bundle_key.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -79,7 +79,7 @@ function field_test_schema() {
     'description' => 'The base table for test entities with a bundle.',
     'fields' => array(
       'ftid' => array(
-        'description' => 'The primary indentifier for a test_entity_bundle.',
+        'description' => 'The primary identifier for a test_entity_bundle.',
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,

+ 2 - 2
modules/field_ui/field_ui.admin.inc

@@ -936,7 +936,7 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund
   $field_label_options = array(
     'above' => t('Above'),
     'inline' => t('Inline'),
-    'hidden' => t('<Hidden>'),
+    'hidden' => '<' . t('Hidden') . '>',
   );
   $extra_visibility_options = array(
     'visible' => t('Visible'),
@@ -992,7 +992,7 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund
     );
 
     $formatter_options = field_ui_formatter_options($field['type']);
-    $formatter_options['hidden'] = t('<Hidden>');
+    $formatter_options['hidden'] = '<' . t('Hidden') . '>';
     $table[$name]['format'] = array(
       'type' => array(
         '#type' => 'select',

+ 3 - 3
modules/field_ui/field_ui.info

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 files[] = field_ui.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 1 - 1
modules/field_ui/field_ui.module

@@ -318,7 +318,7 @@ function field_ui_field_attach_create_bundle($entity_type, $bundle) {
 }
 
 /**
- * Determines the adminstration path for a bundle.
+ * Determines the administration path for a bundle.
  */
 function _field_ui_bundle_admin_path($entity_type, $bundle_name) {
   $bundles = field_info_bundles($entity_type);

+ 12 - 4
modules/file/file.field.inc

@@ -186,7 +186,7 @@ function file_field_load($entity_type, $entities, $field, $instances, $langcode,
         $items[$id][$delta] = NULL;
       }
       else {
-        $items[$id][$delta] = array_merge($item, (array) $files[$item['fid']]);
+        $items[$id][$delta] = array_merge((array) $files[$item['fid']], $item);
       }
     }
   }
@@ -215,8 +215,16 @@ function file_field_presave($entity_type, $entity, $field, $instance, $langcode,
   // Make sure that each file which will be saved with this object has a
   // permanent status, so that it will not be removed when temporary files are
   // cleaned up.
-  foreach ($items as $item) {
+  foreach ($items as $delta => $item) {
+    if (empty($item['fid'])) {
+      unset($items[$delta]);
+      continue;
+    }
     $file = file_load($item['fid']);
+    if (empty($file)) {
+      unset($items[$delta]);
+      continue;
+    }
     if (!$file->status) {
       $file->status = FILE_STATUS_PERMANENT;
       file_save($file);
@@ -760,7 +768,7 @@ function file_field_widget_submit($form, &$form_state) {
   $langcode = $element['#language'];
   $parents = $element['#field_parents'];
 
-  $submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2));
+  $submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#parents'], 0, -2));
   foreach ($submitted_values as $delta => $submitted_value) {
     if (!$submitted_value['fid']) {
       unset($submitted_values[$delta]);
@@ -771,7 +779,7 @@ function file_field_widget_submit($form, &$form_state) {
   $submitted_values = array_values($submitted_values);
 
   // Update form_state values.
-  drupal_array_set_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2), $submitted_values);
+  drupal_array_set_nested_value($form_state['values'], array_slice($button['#parents'], 0, -2), $submitted_values);
 
   // Update items.
   $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);

+ 3 - 3
modules/file/file.info

@@ -6,8 +6,8 @@ core = 7.x
 dependencies[] = field
 files[] = tests/file.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 1 - 1
modules/file/file.js

@@ -83,7 +83,7 @@ Drupal.file = Drupal.file || {
           '%filename': this.value.replace('C:\\fakepath\\', ''),
           '%extensions': extensionPattern.replace(/\|/g, ', ')
         });
-        $(this).closest('div.form-managed-file').prepend('<div class="messages error file-upload-js-error">' + error + '</div>');
+        $(this).closest('div.form-managed-file').prepend('<div class="messages error file-upload-js-error" aria-live="polite">' + error + '</div>');
         this.value = '';
         return false;
       }

+ 1 - 2
modules/file/file.module

@@ -246,7 +246,7 @@ function file_ajax_upload() {
     return array('#type' => 'ajax', '#commands' => $commands);
   }
 
-  list($form, $form_state) = ajax_get_form();
+  list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
 
   if (!$form) {
     // Invalid form_build_id.
@@ -284,7 +284,6 @@ function file_ajax_upload() {
   $js = drupal_add_js();
   $settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
 
-  $commands = array();
   $commands[] = ajax_command_replace(NULL, $output, $settings);
   return array('#type' => 'ajax', '#commands' => $commands);
 }

+ 3 - 3
modules/file/tests/file_module_test.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 3 - 3
modules/filter/filter.info

@@ -7,8 +7,8 @@ files[] = filter.test
 required = TRUE
 configure = admin/config/content/formats
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 2 - 2
modules/filter/filter.module

@@ -739,8 +739,8 @@ function filter_list_format($format_id) {
  * @param $text
  *   The text to be filtered.
  * @param $format_id
- *   (optional) The format ID of the text to be filtered. If no format is
- *   assigned, the fallback format will be used. Defaults to NULL.
+ *   (optional) The machine name of the filter format to be used to filter the
+ *   text. Defaults to the fallback format. See filter_fallback_format().
  * @param $langcode
  *   (optional) The language code of the text to be filtered, e.g. 'en' for
  *   English. This allows filters to be language aware so language specific

+ 3 - 3
modules/forum/forum.info

@@ -9,8 +9,8 @@ files[] = forum.test
 configure = admin/structure/forum
 stylesheets[all][] = forum.css
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 3 - 3
modules/help/help.info

@@ -5,8 +5,8 @@ version = VERSION
 core = 7.x
 files[] = help.test
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 8 - 8
modules/image/image.admin.inc

@@ -592,15 +592,15 @@ function image_crop_form($data) {
     '#type' => 'radios',
     '#title' => t('Anchor'),
     '#options' => array(
-      'left-top'      => t('Top') . ' ' . t('Left'),
-      'center-top'    => t('Top') . ' ' . t('Center'),
-      'right-top'     => t('Top') . ' ' . t('Right'),
-      'left-center'   => t('Center') . ' ' . t('Left'),
+      'left-top'      => t('Top left'),
+      'center-top'    => t('Top center'),
+      'right-top'     => t('Top right'),
+      'left-center'   => t('Center left'),
       'center-center' => t('Center'),
-      'right-center'  => t('Center') . ' ' . t('Right'),
-      'left-bottom'   => t('Bottom') . ' ' . t('Left'),
-      'center-bottom' => t('Bottom') . ' ' . t('Center'),
-      'right-bottom'  => t('Bottom') . ' ' . t('Right'),
+      'right-center'  => t('Center right'),
+      'left-bottom'   => t('Bottom left'),
+      'center-bottom' => t('Bottom center'),
+      'right-bottom'  => t('Bottom right'),
     ),
     '#theme' => 'image_anchor',
     '#default_value' => $data['anchor'],

+ 1 - 1
modules/image/image.field.inc

@@ -351,7 +351,7 @@ function image_field_widget_form(&$form, &$form_state, $field, $instance, $langc
   if ($field['cardinality'] == 1) {
     // If there's only one field, return it as delta 0.
     if (empty($elements[0]['#default_value']['fid'])) {
-      $elements[0]['#description'] = theme('file_upload_help', array('description' => $instance['description'], 'upload_validators' => $elements[0]['#upload_validators']));
+      $elements[0]['#description'] = theme('file_upload_help', array('description' => field_filter_xss($instance['description']), 'upload_validators' => $elements[0]['#upload_validators']));
     }
   }
   else {

+ 3 - 3
modules/image/image.info

@@ -7,8 +7,8 @@ dependencies[] = file
 files[] = image.test
 configure = admin/config/media/image-styles
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 9 - 3
modules/image/image.module

@@ -835,7 +835,7 @@ function image_style_deliver($style, $scheme) {
     else {
       $headers = module_invoke_all('file_download', $image_uri);
       if (in_array(-1, $headers) || empty($headers)) {
-        return drupal_access_denied();
+        return MENU_ACCESS_DENIED;
       }
       if (count($headers)) {
         foreach ($headers as $name => $value) {
@@ -972,7 +972,9 @@ function image_style_flush($style) {
   // Delete the style directory in each registered wrapper.
   $wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE);
   foreach ($wrappers as $wrapper => $wrapper_data) {
-    file_unmanaged_delete_recursive($wrapper . '://styles/' . $style['name']);
+    if (file_exists($directory = $wrapper . '://styles/' . $style['name'])) {
+      file_unmanaged_delete_recursive($directory);
+    }
   }
 
   // Let other modules update as necessary on flush.
@@ -1010,10 +1012,14 @@ function image_style_flush($style) {
  */
 function image_style_url($style_name, $path) {
   $uri = image_style_path($style_name, $path);
+
+  // The passed-in $path variable can be either a relative path or a full URI.
+  $original_uri = file_uri_scheme($path) ? file_stream_wrapper_uri_normalize($path) : file_build_uri($path);
+
   // The token query is added even if the 'image_allow_insecure_derivatives'
   // variable is TRUE, so that the emitted links remain valid if it is changed
   // back to the default FALSE.
-  $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, file_stream_wrapper_uri_normalize($path)));
+  $token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, $original_uri));
 
   // If not using clean URLs, the image derivative callback is only available
   // with the query string. If the file does not exist, use url() to ensure

+ 13 - 3
modules/image/image.test

@@ -216,10 +216,20 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
     }
     // Add some extra chars to the token.
     $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
-    $this->assertResponse(403, 'Image was inaccessible at the URL wih an invalid token.');
+    $this->assertResponse(403, 'Image was inaccessible at the URL with an invalid token.');
     // Change the parameter name so the token is missing.
     $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url));
-    $this->assertResponse(403, 'Image was inaccessible at the URL wih a missing token.');
+    $this->assertResponse(403, 'Image was inaccessible at the URL with a missing token.');
+
+    // Check that the generated URL is the same when we pass in a relative path
+    // rather than a URI. We need to temporarily switch the default scheme to
+    // match the desired scheme before testing this, then switch it back to the
+    // "temporary" scheme used throughout this test afterwards.
+    variable_set('file_default_scheme', $scheme);
+    $relative_path = file_uri_target($original_uri);
+    $generate_url_from_relative_path = image_style_url($this->style_name, $relative_path);
+    $this->assertEqual($generate_url, $generate_url_from_relative_path, 'Generated URL is the same regardless of whether it came from a relative path or a file URI.');
+    variable_set('file_default_scheme', 'temporary');
 
     // Fetch the URL that generates the file.
     $this->drupalGet($generate_url);
@@ -268,7 +278,7 @@ class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase {
     elseif ($clean_url) {
       // Add some extra chars to the token.
       $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
-      $this->assertResponse(200, 'Existing image was accessible at the URL wih an invalid token.');
+      $this->assertResponse(200, 'Existing image was accessible at the URL with an invalid token.');
     }
 
     // Allow insecure image derivatives to be created for the remainder of this

+ 3 - 3
modules/image/tests/image_module_test.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = image_module_test.module
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 1 - 3
modules/locale/locale.admin.inc

@@ -1242,9 +1242,7 @@ function locale_translate_delete_page($lid) {
   if ($source = db_query('SELECT lid, source FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject()) {
     return drupal_get_form('locale_translate_delete_form', $source);
   }
-  else {
-    return drupal_not_found();
-  }
+  return MENU_NOT_FOUND;
 }
 
 /**

+ 3 - 3
modules/locale/locale.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = locale.test
 configure = admin/config/regional/language
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 1 - 1
modules/locale/locale.test

@@ -1685,7 +1685,7 @@ class LocaleBrowserDetectionTest extends DrupalUnitTestCase {
     );
 
     $test_cases = array(
-      // Equal qvalue for each language, choose the site prefered one.
+      // Equal qvalue for each language, choose the site preferred one.
       'en,en-US,fr-CA,fr,es-MX' => 'en',
       'en-US,en,fr-CA,fr,es-MX' => 'en',
       'fr,en' => 'en',

+ 3 - 3
modules/locale/tests/locale_test.info

@@ -5,8 +5,8 @@ package = Testing
 version = VERSION
 hidden = TRUE
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 2 - 4
modules/menu/menu.admin.inc

@@ -512,8 +512,7 @@ function menu_delete_menu_page($menu) {
   // System-defined menus may not be deleted.
   $system_menus = menu_list_system_menus();
   if (isset($system_menus[$menu['menu_name']])) {
-    drupal_access_denied();
-    return;
+    return MENU_ACCESS_DENIED;
   }
   return drupal_get_form('menu_delete_menu_confirm', $menu);
 }
@@ -622,8 +621,7 @@ function menu_item_delete_page($item) {
   // Links defined via hook_menu may not be deleted. Updated items are an
   // exception, as they can be broken.
   if ($item['module'] == 'system' && !$item['updated']) {
-    drupal_access_denied();
-    return;
+    return MENU_ACCESS_DENIED;
   }
   return drupal_get_form('menu_item_delete_form', $item);
 }

+ 3 - 3
modules/menu/menu.info

@@ -6,8 +6,8 @@ core = 7.x
 files[] = menu.test
 configure = admin/structure/menu
 
-; Information added by drupal.org packaging script on 2013-08-08
-version = "7.23"
+; Information added by Drupal.org packaging script on 2014-05-08
+version = "7.28"
 project = "drupal"
-datestamp = "1375928238"
+datestamp = "1399522731"
 

+ 3 - 3
modules/node/content_types.inc

@@ -255,11 +255,11 @@ function _node_characters($length) {
  */
 function node_type_form_validate($form, &$form_state) {
   $type = new stdClass();
-  $type->type = trim($form_state['values']['type']);
+  $type->type = $form_state['values']['type'];
   $type->name = trim($form_state['values']['name']);
 
   // Work out what the type was before the user submitted this form
-  $old_type = trim($form_state['values']['old_type']);
+  $old_type = $form_state['values']['old_type'];
 
   $types = node_type_get_names();
 
@@ -288,7 +288,7 @@ function node_type_form_submit($form, &$form_state) {
 
   $type = node_type_set_defaults();
 
-  $type->type = trim($form_state['values']['type']);
+  $type->type = $form_state['values']['type'];
   $type->name = trim($form_state['values']['name']);
   $type->orig_type = trim($form_state['values']['orig_type']);
   $type->old_type = isset($form_state['values']['old_type']) ? $form_state['values']['old_type'] : $type->type;

+ 2 - 0
modules/node/node.admin.inc

@@ -471,6 +471,7 @@ function node_admin_nodes() {
   $header['operations'] = array('data' => t('Operations'));
 
   $query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
+  $query->addTag('node_admin_filter');
   node_build_filter_query($query);
 
   if (!user_access('bypass node access')) {
@@ -695,6 +696,7 @@ function node_multiple_delete_confirm($form, &$form_state, $nodes) {
 function node_multiple_delete_confirm_submit($form, &$form_state) {
   if ($form_state['values']['confirm']) {
     node_delete_multiple(array_keys($form_state['values']['nodes']));
+    cache_clear_all();
     $count = count($form_state['values']['nodes']);
     watchdog('content', 'Deleted @count posts.', array('@count' => $count));
     drupal_set_message(format_plural($count, 'Deleted 1 post.', 'Deleted @count posts.'));

+ 21 - 15
modules/node/node.api.php

@@ -1033,9 +1033,17 @@ function hook_node_type_delete($info) {
  * This hook is invoked only on the module that defines the node's content type
  * (use hook_node_delete() to respond to all node deletions).
  *
- * This hook is invoked from node_delete_multiple() after the node has been
- * removed from the node table in the database, before hook_node_delete() is
- * invoked, and before field_attach_delete() is called.
+ * This hook is invoked from node_delete_multiple() before hook_node_delete()
+ * is invoked and before field_attach_delete() is called.
+ *
+ * Note that when this hook is invoked, the changes have not yet been written
+ * to the database, because a database transaction is still in progress. The
+ * transaction is not finalized until the delete operation is entirely
+ * completed and node_delete_multiple() goes out of scope. You should not rely
+ * on data in the database at this time as it is not updated yet. You should
+ * also note that any write/update database queries executed from this hook are
+ * also not committed immediately. Check node_delete_multiple() and
+ * db_transaction() for more info.
  *
  * @param $node
  *   The node that is being deleted.
@@ -1063,21 +1071,19 @@ function hook_delete($node) {
  * @ingroup node_api_hooks
  */
 function hook_prepare($node) {
-  if ($file = file_check_upload($field_name)) {
-    $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
-    if ($file) {
-      if (!image_get_info($file->uri)) {
-        form_set_error($field_name, t('Uploaded file is not a valid image'));
-        return;
-      }
-    }
-    else {
+  $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE));
+  if ($file) {
+    if (!image_get_info($file->uri)) {
+      form_set_error($field_name, t('Uploaded file is not a valid image'));
       return;
     }
-    $node->images['_original'] = $file->uri;
-    _image_build_derivatives($node, TRUE);
-    $node->new_file = TRUE;
   }
+  else {
+    return;
+  }
+  $node->images['_original'] = $file->uri;
+  _image_build_derivatives($node, TRUE);
+  $node->new_file = TRUE;
 }
 
 /**

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