Browse Source

drupal core updated to 7.28

Bachir Soussi Chiadmi 10 năm trước cách đây
mục cha
commit
c3011cef61
100 tập tin đã thay đổi với 1243 bổ sung1884 xóa
  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;
 }
 
 /**

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác