Drupal 7.12
authorwebchick
Wed, 1 Feb 2012 21:46:20 +0000 (13:46 -0800)
committerwebchick
Wed, 1 Feb 2012 21:46:20 +0000 (13:46 -0800)
223 files changed:
CHANGELOG.txt
INSTALL.mysql.txt
MAINTAINERS.txt
UPGRADE.txt
includes/actions.inc
includes/ajax.inc
includes/archiver.inc
includes/authorize.inc
includes/batch.inc
includes/batch.queue.inc
includes/bootstrap.inc
includes/cache-install.inc
includes/cache.inc
includes/common.inc
includes/database/database.inc
includes/database/mysql/database.inc
includes/database/pgsql/database.inc
includes/database/query.inc
includes/database/select.inc
includes/database/sqlite/database.inc
includes/file.inc
includes/filetransfer/filetransfer.inc
includes/form.inc
includes/graph.inc
includes/image.inc
includes/install.core.inc
includes/install.inc
includes/iso.inc
includes/json-encode.inc [new file with mode: 0644]
includes/locale.inc
includes/mail.inc
includes/menu.inc
includes/module.inc
includes/password.inc
includes/path.inc
includes/registry.inc
includes/stream_wrappers.inc
includes/theme.inc
includes/token.inc
includes/update.inc
includes/updater.inc
includes/utility.inc
includes/xmlrpc.inc
includes/xmlrpcs.inc
misc/ajax.js
misc/authorize.js
misc/autocomplete.js
misc/drupal.js
modules/aggregator/aggregator-feed-source.tpl.php
modules/aggregator/aggregator-item.tpl.php
modules/aggregator/aggregator-summary-item.tpl.php
modules/aggregator/aggregator-summary-items.tpl.php
modules/aggregator/aggregator-wrapper.tpl.php
modules/aggregator/aggregator.admin.inc
modules/aggregator/aggregator.api.php
modules/aggregator/aggregator.module
modules/aggregator/aggregator.pages.inc
modules/aggregator/aggregator.parser.inc
modules/aggregator/aggregator.processor.inc
modules/aggregator/aggregator.test
modules/block/block-admin-display-form.tpl.php
modules/block/block.admin.inc
modules/block/block.module
modules/block/block.test
modules/block/block.tpl.php
modules/block/tests/block_test.module
modules/block/tests/themes/block_test_theme/block_test_theme.info [new file with mode: 0644]
modules/block/tests/themes/block_test_theme/page.tpl.php [new file with mode: 0644]
modules/color/color-rtl.css
modules/color/color.css
modules/color/color.js
modules/color/color.module
modules/color/color.test
modules/color/preview.js
modules/comment/comment.install
modules/comment/comment.module
modules/comment/comment.test
modules/contact/contact.admin.inc
modules/contact/contact.module
modules/contact/contact.pages.inc
modules/contact/contact.test
modules/contextual/contextual-rtl.css
modules/contextual/contextual.css
modules/contextual/contextual.js
modules/contextual/contextual.module
modules/field/field.api.php
modules/field/field.attach.inc
modules/field/field.crud.inc
modules/field/field.info.inc
modules/field/field.multilingual.inc
modules/field/tests/field.test
modules/field/tests/field_test.entity.inc
modules/field/tests/field_test.field.inc
modules/field/tests/field_test.module
modules/field_ui/field_ui-rtl.css
modules/field_ui/field_ui.admin.inc
modules/field_ui/field_ui.api.php
modules/field_ui/field_ui.css
modules/field_ui/field_ui.js
modules/field_ui/field_ui.module
modules/field_ui/field_ui.test
modules/file/file.api.php
modules/file/file.css
modules/file/file.field.inc
modules/file/file.module
modules/file/tests/file.test
modules/file/tests/file_module_test.module
modules/forum/forum.module
modules/help/help.admin.inc
modules/help/help.api.php
modules/help/help.test
modules/image/image.effects.inc
modules/image/image.field.inc
modules/image/image.install
modules/image/image.test
modules/locale/locale.module
modules/locale/locale.test
modules/locale/tests/locale_test.js
modules/menu/menu.admin.inc
modules/menu/menu.admin.js
modules/menu/menu.install
modules/menu/menu.js
modules/menu/menu.module
modules/menu/menu.test
modules/node/node.api.php
modules/node/node.module
modules/node/node.test
modules/node/tests/node_access_test.module
modules/overlay/overlay-child-rtl.css
modules/overlay/overlay-parent.js
modules/overlay/overlay.module
modules/php/php.module
modules/php/php.test
modules/poll/poll.module
modules/poll/poll.test
modules/profile/profile.module
modules/profile/profile.test
modules/rdf/rdf.module
modules/search/search.api.php
modules/search/search.extender.inc
modules/search/search.module
modules/search/search.test
modules/shortcut/shortcut.admin.js
modules/simpletest/drupal_web_test_case.php
modules/simpletest/simpletest.module
modules/simpletest/simpletest.pages.inc
modules/simpletest/simpletest.test
modules/simpletest/tests/ajax.test
modules/simpletest/tests/ajax_forms_test.module
modules/simpletest/tests/bootstrap.test
modules/simpletest/tests/common.test
modules/simpletest/tests/common_test.module
modules/simpletest/tests/database_test.test
modules/simpletest/tests/entity_cache_test_dependency.module
modules/simpletest/tests/entity_query.test
modules/simpletest/tests/file.test
modules/simpletest/tests/file_test.module
modules/simpletest/tests/form.test
modules/simpletest/tests/form_test.module
modules/simpletest/tests/mail.test
modules/simpletest/tests/path.test
modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_core_version_dependencies_test.module [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_core_version_test.info [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_core_version_test.module [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_module_version_dependencies_test.module [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_module_version_test.info [new file with mode: 0644]
modules/simpletest/tests/system_incompatible_module_version_test.module [new file with mode: 0644]
modules/simpletest/tests/system_test.module
modules/simpletest/tests/theme.test
modules/simpletest/tests/theme_test.module
modules/simpletest/tests/theme_test.template_test.tpl.php [new file with mode: 0644]
modules/simpletest/tests/themes/test_theme/template.php [moved from themes/tests/test_theme/template.php with 100% similarity]
modules/simpletest/tests/themes/test_theme/test_theme.info [moved from themes/tests/test_theme/test_theme.info with 100% similarity]
modules/simpletest/tests/unicode.test
modules/simpletest/tests/update_script_test.info [new file with mode: 0644]
modules/simpletest/tests/update_script_test.install [new file with mode: 0644]
modules/simpletest/tests/update_script_test.module [new file with mode: 0644]
modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php [new file with mode: 0644]
modules/simpletest/tests/upgrade/drupal-7.bare.minimal.database.php.gz [new file with mode: 0644]
modules/simpletest/tests/upgrade/drupal-7.bare.standard_all.database.php.gz [new file with mode: 0644]
modules/simpletest/tests/upgrade/drupal-7.filled.minimal.database.php.gz [new file with mode: 0644]
modules/simpletest/tests/upgrade/drupal-7.filled.standard_all.database.php.gz [new file with mode: 0644]
modules/simpletest/tests/upgrade/upgrade.node.test
modules/simpletest/tests/upgrade/upgrade.test
modules/simpletest/tests/url_alter_test.module
modules/statistics/statistics.module
modules/statistics/statistics.test
modules/syslog/syslog.module
modules/system/system.admin.inc
modules/system/system.api.php
modules/system/system.base.css
modules/system/system.install
modules/system/system.mail.inc
modules/system/system.module
modules/system/system.queue.inc
modules/system/system.test
modules/taxonomy/taxonomy.module
modules/taxonomy/taxonomy.pages.inc
modules/taxonomy/taxonomy.test
modules/toolbar/toolbar.module
modules/tracker/tracker.test
modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info [moved from themes/tests/update_test_basetheme/update_test_basetheme.info with 100% similarity]
modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info [moved from themes/tests/update_test_subtheme/update_test_subtheme.info with 100% similarity]
modules/update/tests/update_test.module
modules/update/update.manager.inc
modules/update/update.module
modules/user/user.install
modules/user/user.js
modules/user/user.module
profiles/standard/standard.install
scripts/dump-database-d7.sh [new file with mode: 0644]
scripts/generate-d7-content.sh [new file with mode: 0644]
scripts/run-tests.sh
scripts/test.script [new file with mode: 0644]
sites/default/default.settings.php
themes/bartik/css/style-rtl.css
themes/seven/maintenance-page.tpl.php
themes/seven/page.tpl.php
themes/seven/style.css
themes/tests/README.txt [deleted file]
update.php

index 76d7f8a..b68b7fa 100644 (file)
@@ -1,4 +1,41 @@
 
+Drupal 7.13 xxxx-xx-xx (development version)
+----------------------
+
+Drupal 7.12, 2012-02-01
+----------------------
+- Fixed bug preventing custom menus from receiving an active trail.
+- Fixed hook_field_delete() no longer invoked during field_purge_data().
+- Fixed bug causing entity info cache to not be cleared with the rest of caches.
+- Fixed file_unmanaged_copy() fails with Drupal 7.7+ and safe_mode() or
+  open_basedir().
+- Fixed Nested transactions throw exceptions when they got out of scope.
+- Fixed bugs with the Return-Path when sending mail on both Windows and
+  non-Windows systems.
+- Fixed bug with DrupalCacheArray property visibility preventing others from
+  extending it (API change: http://drupal.org/node/1422264).
+- Fixed bug with handling of non-ASCII characters in file names (API change:
+  http://drupal.org/node/1424840).
+- Reconciled field maximum length with database column size in image and
+  aggregator modules.
+- Fixes to various core JavaScript files to allow for minification and
+  aggregation.
+- Fixed Prevent tests from deleting main installation's tables when
+  parent::setUp() is not called.
+- Fixed several Poll module bugs.
+- Fixed several Shortcut module bugs.
+- Added new hook_system_theme_info() to provide ability for contributed modules
+  to test theme functionality.
+- Added ability to cancel mail sending from hook_mail_alter().
+- Added support for configurable PDO connection options, enabling master-master
+  database replication.
+- Numerous improvements to tests and test runner to pave the way for faster test
+  runs.
+- Expanded test coverage.
+- Numerous API documentation improvements.
+- Numerous performance improvements, including token replacement and render
+  cache.
+
 Drupal 7.11, 2012-02-01
 ----------------------
 - Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
index a7c292e..bee5811 100644 (file)
@@ -18,7 +18,7 @@ initial database files. Next you must log in and set the access database rights:
   mysql -u username -p
 
 Again, you will be asked for the 'username' database password. At the MySQL
-prompt, enter following command:
+prompt, enter the following command:
 
   GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
   ON databasename.*
index 30fa827..35eecf8 100644 (file)
@@ -1,16 +1,14 @@
 
-Drupal core is maintained by the community.  To participate, go to
-
-  http://drupal.org/contribute
-
-The people listed here have agreed to do more quality assurance work for
-particular areas of Drupal.  All of them are subject to change.
-
+Drupal core is built and maintained by the Drupal project community. Everyone is
+encouraged to submit issues and changes (patches) to improve Drupal, and to
+contribute in other ways -- see http://drupal.org/contribute to find out how.
 
 Branch maintainers
 ------------------
 
-Drupal 7
+The Drupal Core branch maintainers oversee the development of Drupal as a whole.
+The branch maintainers for Drupal 7 are:
+
 - Dries Buytaert 'dries' <http://drupal.org/user/1>
 - Angela Byron 'webchick' <http://drupal.org/user/24967>
 
@@ -18,6 +16,11 @@ Drupal 7
 Component maintainers
 ---------------------
 
+The Drupal Core component maintainers oversee the development of Drupal
+subsystems. See http://drupal.org/contribute/core-maintainers for more
+information on their responsibilities, and to find out how to become a component
+maintainer. Current component maintainers for Drupal 7:
+
 Ajax system
 - Alex Bronstein 'effulgentsia' <http://drupal.org/user/78040>
 - Randy Fay 'rfay' <http://drupal.org/user/30906>
@@ -130,7 +133,6 @@ Accessibility
 - Brandon Bowersox 'brandonojc' <http://drupal.org/user/186415> 
 
 Documentation
-- Ariane Khachatourians 'arianek' <http://drupal.org/user/158886>
 - Jennifer Hodgdon 'jhodgdon' <http://drupal.org/user/155601>
 
 Security
@@ -254,6 +256,7 @@ System module
 - ?
 
 Taxonomy module
+- Jess Myrbo 'xjm' <http://drupal.org/user/65776>
 - Nathaniel Catchpole 'catch' <http://drupal.org/user/35733>
 - Benjamin Doherty 'bangpound' <http://drupal.org/user/100456>
 
index c6fee98..c993df7 100644 (file)
@@ -141,15 +141,19 @@ following the instructions in the INTRODUCTION section at the top of this file:
    download Drupal 6.x and follow the instructions in its UPGRADE.txt. This
    document only applies for upgrades from 6.x to 7.x.
 
-3. Log in as user ID 1 (the site maintenance user).
+3. In addition to updating to the latest available version of Drupal 7.x core,
+   you must also upgrade all of your contributed modules for Drupal to their
+   latest Drupal 6.x versions.
 
-4. Go to Administer > Site configuration > Site maintenance. Select
+4. Log in as user ID 1 (the site maintenance user).
+
+5. Go to Administer > Site configuration > Site maintenance. Select
    "Off-line" and save the configuration.
 
-5. Go to Administer > Site building > Themes. Enable "Garland" and select it as
+6. Go to Administer > Site building > Themes. Enable "Garland" and select it as
    the default theme.
 
-6. Go to Administer > Site building > Modules. Disable all modules that are not
+7. Go to Administer > Site building > Modules. Disable all modules that are not
    listed under "Core - required" or "Core - optional". It is possible that some
    modules cannot be disabled, because others depend on them. Repeat this step
    until all non-core modules are disabled.
@@ -158,21 +162,21 @@ following the instructions in the INTRODUCTION section at the top of this file:
    no longer need their data, then you can uninstall them under the Uninstall
    tab after disabling them.
 
-7. On the command line or in your FTP client, remove the file
+8. On the command line or in your FTP client, remove the file
 
      sites/default/default.settings.php
 
-8. Remove all old core files and directories, except for the 'sites' directory
+9. Remove all old core files and directories, except for the 'sites' directory
    and any custom files you added elsewhere.
 
    If you made modifications to files like .htaccess or robots.txt, you will
    need to re-apply them from your backup, after the new files are in place.
 
-9. If you uninstalled any modules, remove them from the sites/all/modules and
+10. If you uninstalled any modules, remove them from the sites/all/modules and
    other sites/*/modules directories. Leave other modules in place, even though
    they are incompatible with Drupal 7.x.
 
-10. Download the latest Drupal 7.x release from http://drupal.org to a
+11. Download the latest Drupal 7.x release from http://drupal.org to a
    directory outside of your web root. Extract the archive and copy the files
    into your Drupal directory.
 
@@ -191,14 +195,14 @@ following the instructions in the INTRODUCTION section at the top of this file:
    from http://drupal.org using your web browser, extract it, and then use an
    FTP client to upload the files to your web root.
 
-11. Re-apply any modifications to files such as .htaccess or robots.txt.
+12. Re-apply any modifications to files such as .htaccess or robots.txt.
 
-12. Make your settings.php file writeable, so that the update process can
+13. Make your settings.php file writeable, so that the update process can
    convert it to the format of Drupal 7.x. settings.php is usually located in
 
      sites/default/settings.php
 
-13. Run update.php by visiting http://www.example.com/update.php (replace
+14. Run update.php by visiting http://www.example.com/update.php (replace
    www.example.com with your domain name). This will update the core database
    tables.
 
@@ -214,17 +218,17 @@ following the instructions in the INTRODUCTION section at the top of this file:
 
    - Once the upgrade is done, $update_free_access must be reverted to FALSE.
 
-14. Backup your database after the core upgrade has run.
+15. Backup your database after the core upgrade has run.
 
-15. Replace and update your non-core modules and themes, following the
+16. Replace and update your non-core modules and themes, following the
    procedures at http://drupal.org/node/948216
 
-16. Go to Administration > Reports > Status report. Verify that everything is
+17. Go to Administration > Reports > Status report. Verify that everything is
    working as expected.
 
-17. Ensure that $update_free_access is FALSE in settings.php.
+18. Ensure that $update_free_access is FALSE in settings.php.
 
-18. Go to Administration > Configuration > Development > Maintenance mode.
+19. Go to Administration > Configuration > Development > Maintenance mode.
    Disable the "Put site into maintenance mode" checkbox and save the
    configuration.
 
index 760de83..ed43af4 100644 (file)
@@ -22,7 +22,7 @@
  * - $a1, $a2: Optional additional information, which can be passed into
  *   actions_do() and will be passed along to the action function.
  *
- * @} End of "defgroup actions".
+ * @}
  */
 
 /**
@@ -48,6 +48,7 @@
  *   Passed along to the callback.
  * @param $a2
  *   Passed along to the callback.
+ *
  * @return
  *   An associative array containing the results of the functions that
  *   perform the actions, keyed on action ID.
@@ -149,6 +150,7 @@ function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a
  *
  * @param $reset
  *   Reset the action info static cache.
+ *
  * @return
  *   An associative array keyed on action function name, with the same format
  *   as the return value of hook_action_info(), containing all
@@ -176,9 +178,9 @@ function actions_list($reset = FALSE) {
  * function and the actions returned by actions_list() are partially
  * synchronized. Non-configurable actions from hook_action_info()
  * implementations are put into the database when actions_synchronize() is
- * called, which happens when admin/config/system/actions is visited. Configurable
- * actions are not added to the database until they are configured in the
- * user interface, in which case a database row is created for each
+ * called, which happens when admin/config/system/actions is visited.
+ * Configurable actions are not added to the database until they are configured
+ * in the user interface, in which case a database row is created for each
  * configuration of each action.
  *
  * @return
@@ -205,6 +207,7 @@ function actions_get_all_actions() {
  *   An associative array with function names or action IDs as keys
  *   and associative arrays with keys 'label', 'type', etc. as values.
  *   This is usually the output of actions_list() or actions_get_all_actions().
+ *
  * @return
  *   An associative array whose keys are hashes of the input array keys, and
  *   whose corresponding values are associative arrays with components
@@ -223,7 +226,7 @@ function actions_actions_map($actions) {
 }
 
 /**
- * Given a hash of an action array key, returns the key (function or ID).
+ * Returns an action array key (function or ID), given its hash.
  *
  * Faster than actions_actions_map() when you only need the function name or ID.
  *
@@ -231,6 +234,7 @@ function actions_actions_map($actions) {
  *   Hash of a function name or action ID array key. The array key
  *   is a key into the return value of actions_list() (array key is the action
  *   function name) or actions_get_all_actions() (array key is the action ID).
+ *
  * @return
  *   The corresponding array key, or FALSE if no match is found.
  */
@@ -332,6 +336,7 @@ function actions_synchronize($delete_orphans = FALSE) {
  *   to Jim'.
  * @param $aid
  *   The ID of this action. If omitted, a new action is created.
+ *
  * @return
  *   The ID of the action.
  */
@@ -361,6 +366,7 @@ function actions_save($function, $type, $params, $label, $aid = NULL) {
  *
  * @param $aid
  *   The ID of the action to retrieve.
+ *
  * @return
  *   The appropriate action row from the database as an object.
  */
@@ -380,4 +386,3 @@ function actions_delete($aid) {
     ->execute();
   module_invoke_all('actions_delete', $aid);
 }
-
index d70808e..fb07477 100644 (file)
@@ -24,7 +24,8 @@
  * ajax_form_callback() and a defined #ajax['callback'] function.
  * However, you may optionally specify a different path to request or a
  * different callback function to invoke, which can return updated HTML or can
- * also return a richer set of @link ajax_commands Ajax framework commands @endlink.
+ * also return a richer set of
+ * @link ajax_commands Ajax framework commands @endlink.
  *
  * Standard form handling is as follows:
  *   - A form element has a #ajax property that includes #ajax['callback'] and
  * In the above example, the 'changethis' element is Ajax-enabled. The default
  * #ajax['event'] is 'change', so when the 'changethis' element changes,
  * an Ajax call is made. The form is submitted and reprocessed, and then the
- * callback is called.  In this case, the form has been automatically
+ * callback is called. In this case, the form has been automatically
  * built changing $form['replace_textfield']['#description'], so the callback
  * just returns that part of the form.
  *
  * be converted to a JSON object and returned to the client, which will then
  * iterate over the array and process it like a macro language.
  *
- * Each command item is an associative array which will be converted to a command
- * object on the JavaScript side. $command_item['command'] is the type of
- * command, e.g. 'alert' or 'replace', and will correspond to a method in the
- * Drupal.ajax[command] space. The command array may contain any other data
- * that the command needs to process, e.g. 'method', 'selector', 'settings', etc.
+ * Each command item is an associative array which will be converted to a
+ * command object on the JavaScript side. $command_item['command'] is the type
+ * of command, e.g. 'alert' or 'replace', and will correspond to a method in the
+ * Drupal.ajax[command] space. The command array may contain any other data that
+ * the command needs to process, e.g. 'method', 'selector', 'settings', etc.
  *
  * Commands are usually created with a couple of helper functions, so they
  * look like this:
  */
 
 /**
- * Render a commands array into JSON.
+ * Renders a commands array into JSON.
  *
  * @param $commands
  *   A list of macro commands generated by the use of ajax_command_*()
@@ -262,19 +263,13 @@ function ajax_render($commands = array()) {
     }
   }
 
-  // Settings are handled separately, later in this function, so that changes to
-  // the ajaxPageState setting that occur during drupal_get_css() and
-  // drupal_get_js() get included, and because the jQuery.extend() code produced
-  // by drupal_get_js() for adding settings isn't appropriate during an Ajax
-  // response, because it does not pass TRUE for the "deep" parameter, and
-  // therefore, can clobber existing settings on the page.
+  // Render the HTML to load these files, and add AJAX commands to insert this
+  // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
+  // data from being altered again, as we already altered it above. Settings are
+  // handled separately, afterwards.
   if (isset($items['js']['settings'])) {
     unset($items['js']['settings']);
   }
-
-  // Render the HTML to load these files, and add Ajax commands to insert this
-  // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
-  // data from being altered again, as we already altered it above.
   $styles = drupal_get_css($items['css'], TRUE);
   $scripts_footer = drupal_get_js('footer', $items['js'], TRUE);
   $scripts_header = drupal_get_js('header', $items['js'], TRUE);
@@ -293,11 +288,10 @@ function ajax_render($commands = array()) {
     $commands = array_merge($extra_commands, $commands);
   }
 
+  // Now add a command to merge changes and additions to Drupal.settings.
   $scripts = drupal_add_js();
   if (!empty($scripts['settings'])) {
     $settings = $scripts['settings'];
-    // Automatically extract any settings added via drupal_add_js() and make
-    // them the first command.
     array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
   }
 
@@ -308,7 +302,7 @@ function ajax_render($commands = array()) {
 }
 
 /**
- * Get a form submitted via #ajax during an Ajax callback.
+ * Gets a form submitted via #ajax during an Ajax callback.
  *
  * This will load a form from the form cache used during Ajax operations. It
  * pulls the form info from $_POST.
@@ -368,6 +362,8 @@ function ajax_get_form() {
  * #ajax['path']. If processing is required that cannot be accomplished with
  * a callback, re-implement this function and set #ajax['path'] to the
  * enhanced function.
+ *
+ * @see system_menu()
  */
 function ajax_form_callback() {
   list($form, $form_state) = ajax_get_form();
@@ -403,6 +399,9 @@ function ajax_form_callback() {
  * of the page. Therefore, system_menu() sets the 'theme callback' for
  * 'system/ajax' to this function, and it is recommended that modules
  * implementing other generic Ajax paths do the same.
+ *
+ * @see system_menu()
+ * @see file_menu()
  */
 function ajax_base_page_theme() {
   if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) {
@@ -421,7 +420,7 @@ function ajax_base_page_theme() {
 }
 
 /**
- * Package and send the result of a page callback to the browser as an Ajax response.
+ * Packages and sends the result of a page callback as an Ajax response.
  *
  * This function is the equivalent of drupal_deliver_html_page(), but for Ajax
  * requests. Like that function, it:
@@ -554,7 +553,7 @@ function ajax_prepare_response($page_callback_result) {
 }
 
 /**
- * Perform end-of-Ajax-request tasks.
+ * Performs end-of-Ajax-request tasks.
  *
  * This function is the equivalent of drupal_page_footer(), but for Ajax
  * requests.
@@ -577,7 +576,7 @@ function ajax_footer() {
 }
 
 /**
- * Form element process callback to handle #ajax.
+ * Form element processing handler for the #ajax form property.
  *
  * @param $element
  *   An associative array containing the properties of the element.
@@ -596,7 +595,7 @@ function ajax_process_form($element, &$form_state) {
 }
 
 /**
- * Add Ajax information about an element to the page to communicate with JavaScript.
+ * Adds Ajax information about an element to communicate with JavaScript.
  *
  * If #ajax['path'] is set on an element, this additional JavaScript is added
  * to the page header to attach the Ajax behaviors. See ajax.js for more
@@ -1210,4 +1209,3 @@ function ajax_command_restripe($selector) {
     'selector' => $selector,
   );
 }
-
index fec053b..3ce1173 100644 (file)
@@ -6,61 +6,63 @@
  */
 
 /**
- * Common interface for all Archiver classes.
+ * Defines the common interface for all Archiver classes.
  */
 interface ArchiverInterface {
 
   /**
-   * Constructor for a new archiver instance.
+   * Constructs a new archiver instance.
    *
    * @param $file_path
-   *   The full system path of the archive to manipulate.  Only local files
-   *   are supported.  If the file does not yet exist, it will be created if
+   *   The full system path of the archive to manipulate. Only local files
+   *   are supported. If the file does not yet exist, it will be created if
    *   appropriate.
    */
   public function __construct($file_path);
 
   /**
-   * Add the specified file or directory to the archive.
+   * Adds the specified file or directory to the archive.
    *
    * @param $file_path
    *   The full system path of the file or directory to add. Only local files
    *   and directories are supported.
+   *
    * @return ArchiverInterface
    *   The called object.
    */
   public function add($file_path);
 
   /**
-   * Remove the specified file from the archive.
+   * Removes the specified file from the archive.
    *
    * @param $path
    *   The file name relative to the root of the archive to remove.
+   *
    * @return ArchiverInterface
    *   The called object.
    */
   public function remove($path);
 
   /**
-   * Extract multiple files in the archive to the specified path.
+   * Extracts multiple files in the archive to the specified path.
    *
    * @param $path
    *   A full system path of the directory to which to extract files.
    * @param $files
    *   Optionally specify a list of files to be extracted. Files are
    *   relative to the root of the archive. If not specified, all files
-   *   in the archive will be extracted
+   *   in the archive will be extracted.
+   *
    * @return ArchiverInterface
    *   The called object.
    */
-  public function extract($path, Array $files = array());
+  public function extract($path, array $files = array());
 
   /**
-   * List all files in the archive.
+   * Lists all files in the archive.
    *
    * @return
    *   An array of file names relative to the root of the archive.
    */
   public function listContents();
 }
-
index 8528604..da6918c 100644 (file)
@@ -6,7 +6,13 @@
  */
 
 /**
- * Build the form for choosing a FileTransfer type and supplying credentials.
+ * Form constructor for the file transfer authorization form.
+ *
+ * Allows the user to choose a FileTransfer type and supply credentials.
+ *
+ * @see authorize_filetransfer_form_validate()
+ * @see authorize_filetransfer_form_submit()
+ * @ingroup forms
  */
 function authorize_filetransfer_form($form, &$form_state) {
   global $base_url, $is_https;
@@ -127,10 +133,11 @@ function authorize_filetransfer_form($form, &$form_state) {
 }
 
 /**
- * Generate the Form API array for the settings for a given connection backend.
+ * Generates the Form API array for a given connection backend's settings.
  *
  * @param $backend
  *   The name of the backend (e.g. 'ftp', 'ssh', etc).
+ *
  * @return
  *   Form API array of connection settings for the given backend.
  *
@@ -151,7 +158,7 @@ function _authorize_filetransfer_connection_settings($backend) {
 }
 
 /**
- * Recursively fill in the default settings on a file transfer connection form.
+ * Sets the default settings on a file transfer connection form recursively.
  *
  * The default settings for the file transfer connection forms are saved in
  * the database. The settings are stored as a nested array in the case of a
@@ -165,8 +172,6 @@ function _authorize_filetransfer_connection_settings($backend) {
  *   The key for our current form element, if any.
  * @param array $defaults
  *   The default settings for the file transfer backend we're operating on.
- * @return
- *   Nothing, this function just sets $element['#default_value'] if needed.
  */
 function _authorize_filetransfer_connection_settings_set_defaults(&$element, $key, array $defaults) {
   // If we're operating on a form element which isn't a fieldset, and we have
@@ -186,9 +191,10 @@ function _authorize_filetransfer_connection_settings_set_defaults(&$element, $ke
 }
 
 /**
- * Validate callback for the filetransfer authorization form.
+ * Form validation handler for authorize_filetransfer_form().
  *
  * @see authorize_filetransfer_form()
+ * @see authorize_filetransfer_submit()
  */
 function authorize_filetransfer_form_validate($form, &$form_state) {
   // Only validate the form if we have collected all of the user input and are
@@ -218,9 +224,10 @@ function authorize_filetransfer_form_validate($form, &$form_state) {
 }
 
 /**
- * Submit callback when a file transfer is being authorized.
+ * Form submission handler for authorize_filetransfer_form().
  *
  * @see authorize_filetransfer_form()
+ * @see authorize_filetransfer_validate()
  */
 function authorize_filetransfer_form_submit($form, &$form_state) {
   global $base_url;
@@ -280,7 +287,7 @@ function authorize_filetransfer_form_submit($form, &$form_state) {
 }
 
 /**
- * Run the operation specified in $_SESSION['authorize_operation']
+ * Runs the operation specified in $_SESSION['authorize_operation'].
  *
  * @param $filetransfer
  *   The FileTransfer object to use for running the operation.
@@ -298,12 +305,13 @@ function authorize_run_operation($filetransfer) {
 }
 
 /**
- * Get a FileTransfer class for a specific transfer method and settings.
+ * Gets a FileTransfer class for a specific transfer method and settings.
  *
  * @param $backend
  *   The FileTransfer backend to get the class for.
  * @param $settings
  *   Array of settings for the FileTransfer.
+ *
  * @return
  *   An instantiated FileTransfer object for the requested method and settings,
  *   or FALSE if there was an error finding or instantiating it.
index 727c625..061acd4 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-
 /**
  * @file
  * Batch processing API for processes to run in multiple HTTP requests.
@@ -21,6 +20,7 @@
  * @param $id
  *   The ID of the batch to load. When a progressive batch is being processed,
  *   the relevant ID is found in $_REQUEST['id'].
+ *
  * @return
  *   An array representing the batch, or FALSE if no batch was found.
  */
@@ -36,7 +36,7 @@ function batch_load($id) {
 }
 
 /**
- * State-based dispatcher for the batch processing page.
+ * Renders the batch processing page based on the current state of the batch.
  *
  * @see _batch_shutdown()
  */
@@ -94,7 +94,7 @@ function _batch_page() {
 }
 
 /**
- * Initialize the batch processing.
+ * Initializes the batch processing.
  *
  * JavaScript-enabled clients are identified by the 'has_js' cookie set in
  * drupal.js. If no JavaScript-enabled page has been visited during the current
@@ -110,7 +110,7 @@ function _batch_start() {
 }
 
 /**
- * Output a batch processing page with JavaScript support.
+ * Outputs a batch processing page with JavaScript support.
  *
  * This initializes the batch and error messages. Note that in JavaScript-based
  * processing, the batch processing page is displayed only once and updated via
@@ -144,7 +144,7 @@ function _batch_progress_page_js() {
 }
 
 /**
- * Do one execution pass in JavaScript-mode and return progress to the browser.
+ * Does one execution pass with JavaScript and returns progress to the browser.
  *
  * @see _batch_progress_page_js()
  * @see _batch_process()
@@ -164,7 +164,7 @@ function _batch_do() {
 }
 
 /**
- * Output a batch processing page without JavaScript support.
+ * Outputs a batch processing page without JavaScript support.
  *
  * @see _batch_process()
  */
@@ -228,7 +228,7 @@ function _batch_progress_page_nojs() {
 }
 
 /**
- * Process sets in a batch.
+ * Processes sets in a batch.
  *
  * If the batch was marked for progressive execution (default), this executes as
  * many operations in batch sets until an execution time of 1 second has been
@@ -370,7 +370,7 @@ function _batch_process() {
 }
 
 /**
- * Helper function for _batch_process(): returns the formatted percentage.
+ * Formats the percent completion for a batch set.
  *
  * @param $total
  *   The total number of operations.
@@ -379,11 +379,14 @@ function _batch_process() {
  *   rather than an integer in the case of a multi-step operation that is not
  *   yet complete; in that case, the fractional part of $current represents the
  *   fraction of the operation that has been completed.
+ *
  * @return
  *   The properly formatted percentage, as a string. We output percentages
  *   using the correct number of decimal places so that we never print "100%"
  *   until we are finished, but we also never print more decimal places than
  *   are meaningful.
+ *
+ * @see _batch_process()
  */
 function _batch_api_percentage($total, $current) {
   if (!$total || $total == $current) {
@@ -410,7 +413,7 @@ function _batch_api_percentage($total, $current) {
 }
 
 /**
- * Return the batch set being currently processed.
+ * Returns the batch set being currently processed.
  */
 function &_batch_current_set() {
   $batch = &batch_get();
@@ -418,7 +421,7 @@ function &_batch_current_set() {
 }
 
 /**
- * Retrieve the next set in a batch.
+ * Retrieves the next set in a batch.
  *
  * If there is a subsequent set in this batch, assign it as the new set to
  * process and execute its form submit handler (if defined), which may add
@@ -442,7 +445,7 @@ function _batch_next_set() {
 }
 
 /**
- * End the batch processing.
+ * Ends the batch processing.
  *
  * Call the 'finished' callback of each batch set to allow custom handling of
  * the results and resolve page redirection.
@@ -521,7 +524,10 @@ function _batch_finished() {
 }
 
 /**
- * Shutdown function; store the current batch data for the next request.
+ * Shutdown function: Stores the current batch data for the next request.
+ *
+ * @see _batch_page()
+ * @see drupal_register_shutdown_function()
  */
 function _batch_shutdown() {
   if ($batch = batch_get()) {
@@ -531,4 +537,3 @@ function _batch_shutdown() {
       ->execute();
   }
 }
-
index 8464836..ed290ee 100644 (file)
@@ -1,24 +1,30 @@
 <?php
 
-
 /**
  * @file
  * Queue handlers used by the Batch API.
  *
- * Those implementations:
- * - ensure FIFO ordering,
- * - let an item be repeatedly claimed until it is actually deleted (no notion
- *   of lease time or 'expire' date), to allow multipass operations.
+ * These implementations:
+ * - Ensure FIFO ordering.
+ * - Allow an item to be repeatedly claimed until it is actually deleted (no
+ *   notion of lease time or 'expire' date), to allow multipass operations.
  */
 
 /**
- * Batch queue implementation.
+ * Defines a batch queue.
  *
  * Stale items from failed batches are cleaned from the {queue} table on cron
  * using the 'created' date.
  */
 class BatchQueue extends SystemQueue {
 
+  /**
+   * Overrides SystemQueue::claimItem().
+   *
+   * Unlike SystemQueue::claimItem(), this method provides a default lease
+   * time of 0 (no expiration) instead of 30. This allows the item to be
+   * claimed repeatedly until it is deleted.
+   */
   public function claimItem($lease_time = 0) {
     $item = db_query_range('SELECT data, item_id FROM {queue} q WHERE name = :name ORDER BY item_id ASC', 0, 1, array(':name' => $this->name))->fetchObject();
     if ($item) {
@@ -29,9 +35,9 @@ class BatchQueue extends SystemQueue {
   }
 
   /**
-   * Retrieve all remaining items in the queue.
+   * Retrieves all remaining items in the queue.
    *
-   * This is specific to Batch API and is not part of the DrupalQueueInterface,
+   * This is specific to Batch API and is not part of the DrupalQueueInterface.
    */
   public function getAllItems() {
     $result = array();
@@ -44,10 +50,17 @@ class BatchQueue extends SystemQueue {
 }
 
 /**
- * Batch queue implementation used for non-progressive batches.
+ * Defines a batch queue for non-progressive batches.
  */
 class BatchMemoryQueue extends MemoryQueue {
 
+  /**
+   * Overrides MemoryQueue::claimItem().
+   *
+   * Unlike MemoryQueue::claimItem(), this method provides a default lease
+   * time of 0 (no expiration) instead of 30. This allows the item to be
+   * claimed repeatedly until it is deleted.
+   */
   public function claimItem($lease_time = 0) {
     if (!empty($this->queue)) {
       reset($this->queue);
@@ -57,9 +70,9 @@ class BatchMemoryQueue extends MemoryQueue {
   }
 
   /**
-   * Retrieve all remaining items in the queue.
+   * Retrieves all remaining items in the queue.
    *
-   * This is specific to Batch API and is not part of the DrupalQueueInterface,
+   * This is specific to Batch API and is not part of the DrupalQueueInterface.
    */
   public function getAllItems() {
     $result = array();
index 292b8bb..c32c05d 100644 (file)
@@ -8,7 +8,7 @@
 /**
  * The current system version.
  */
-define('VERSION', '7.11');
+define('VERSION', '7.12');
 
 /**
  * Core API compatibility.
@@ -43,9 +43,9 @@ define('CACHE_TEMPORARY', -1);
  * Logging severity levels as defined in RFC 3164.
  *
  * The WATCHDOG_* constant definitions correspond to the logging severity levels
- * defined in RFC 3164, section 4.1.1.  PHP supplies predefined LOG_* constants
+ * defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants
  * for use in the syslog() function, but their values on Windows builds do not
- * correspond to RFC 3164.  The associated PHP bug report was closed with the
+ * correspond to RFC 3164. The associated PHP bug report was closed with the
  * comment, "And it's also not a bug, as Windows just have less log levels,"
  * and "So the behavior you're seeing is perfectly normal."
  *
@@ -137,8 +137,7 @@ define('DRUPAL_BOOTSTRAP_PAGE_HEADER', 5);
 define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
 
 /**
- * Final bootstrap phase: Drupal is fully loaded; validate and fix
- * input data.
+ * Final bootstrap phase: Drupal is fully loaded; validate and fix input data.
  */
 define('DRUPAL_BOOTSTRAP_FULL', 7);
 
@@ -153,8 +152,9 @@ define('DRUPAL_ANONYMOUS_RID', 1);
 define('DRUPAL_AUTHENTICATED_RID', 2);
 
 /**
- * The number of bytes in a kilobyte. For more information, visit
- * http://en.wikipedia.org/wiki/Kilobyte.
+ * The number of bytes in a kilobyte.
+ *
+ * For more information, visit http://en.wikipedia.org/wiki/Kilobyte.
  */
 define('DRUPAL_KILOBYTE', 1024);
 
@@ -191,10 +191,14 @@ define('LANGUAGE_LTR', 0);
 define('LANGUAGE_RTL', 1);
 
 /**
- * For convenience, define a short form of the request time global.
+ * Time of the current request in seconds elapsed since the Unix Epoch.
+ *
+ * This differs from $_SERVER['REQUEST_TIME'], which is stored as a float
+ * since PHP 5.4.0. Float timestamps confuse most PHP functions
+ * (including date_create()).
  *
- * REQUEST_TIME is a float with microseconds since PHP 5.4.0, but float
- * timestamps confuses most of the PHP functions (including date_create()).
+ * @see http://php.net/manual/reserved.variables.server.php
+ * @see http://php.net/manual/function.time.php
  */
 define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
 
@@ -285,12 +289,12 @@ abstract class DrupalCacheArray implements ArrayAccess {
   /**
    * A cid to pass to cache_set() and cache_get().
    */
-  private $cid;
+  protected $cid;
 
   /**
    * A bin to pass to cache_set() and cache_get().
    */
-  private $bin;
+  protected $bin;
 
   /**
    * An array of keys to add to the cache at the end of the request.
@@ -303,7 +307,7 @@ abstract class DrupalCacheArray implements ArrayAccess {
   protected $storage = array();
 
   /**
-   * Constructor.
+   * Constructs a DrupalCacheArray object.
    *
    * @param $cid
    *   The cid for the array being cached.
@@ -319,10 +323,16 @@ abstract class DrupalCacheArray implements ArrayAccess {
     }
   }
 
+  /**
+   * Implements ArrayAccess::offsetExists().
+   */
   public function offsetExists($offset) {
     return $this->offsetGet($offset) !== NULL;
   }
 
+  /**
+   * Implements ArrayAccess::offsetGet().
+   */
   public function offsetGet($offset) {
     if (isset($this->storage[$offset]) || array_key_exists($offset, $this->storage)) {
       return $this->storage[$offset];
@@ -332,10 +342,16 @@ abstract class DrupalCacheArray implements ArrayAccess {
     }
   }
 
+  /**
+   * Implements ArrayAccess::offsetSet().
+   */
   public function offsetSet($offset, $value) {
     $this->storage[$offset] = $value;
   }
 
+  /**
+   * Implements ArrayAccess::offsetUnset().
+   */
   public function offsetUnset($offset) {
     unset($this->storage[$offset]);
   }
@@ -375,32 +391,31 @@ abstract class DrupalCacheArray implements ArrayAccess {
   abstract protected function resolveCacheMiss($offset);
 
   /**
-   * Immediately write a value to the persistent cache.
+   * Writes a value to the persistent cache immediately.
    *
-   * @param $cid
-   *   The cache ID.
-   * @param $bin
-   *   The cache bin.
    * @param $data
    *   The data to write to the persistent cache.
    * @param $lock
    *   Whether to acquire a lock before writing to cache.
    */
-  protected function set($cid, $data, $bin, $lock = TRUE) {
+  protected function set($data, $lock = TRUE) {
     // Lock cache writes to help avoid stampedes.
     // To implement locking for cache misses, override __construct().
-    $lock_name = $cid . ':' . $bin;
+    $lock_name = $this->cid . ':' . $this->bin;
     if (!$lock || lock_acquire($lock_name)) {
-      if ($cached = cache_get($cid, $bin)) {
+      if ($cached = cache_get($this->cid, $this->bin)) {
         $data = $cached->data + $data;
       }
-      cache_set($cid, $data, $bin);
+      cache_set($this->cid, $data, $this->bin);
       if ($lock) {
         lock_release($lock_name);
       }
     }
   }
 
+  /**
+   * Destructs the DrupalCacheArray object.
+   */
   public function __destruct() {
     $data = array();
     foreach ($this->keysToPersist as $offset => $persist) {
@@ -409,14 +424,16 @@ abstract class DrupalCacheArray implements ArrayAccess {
       }
     }
     if (!empty($data)) {
-      $this->set($this->cid, $data, $this->bin);
+      $this->set($data);
     }
   }
 }
 
 /**
- * Start the timer with the specified name. If you start and stop the same
- * timer multiple times, the measured intervals will be accumulated.
+ * Starts the timer with the specified name.
+ *
+ * If you start and stop the same timer multiple times, the measured intervals
+ * will be accumulated.
  *
  * @param $name
  *   The name of the timer.
@@ -429,7 +446,7 @@ function timer_start($name) {
 }
 
 /**
- * Read the current timer value without stopping the timer.
+ * Reads the current timer value without stopping the timer.
  *
  * @param $name
  *   The name of the timer.
@@ -453,7 +470,7 @@ function timer_read($name) {
 }
 
 /**
- * Stop the timer with the specified name.
+ * Stops the timer with the specified name.
  *
  * @param $name
  *   The name of the timer.
@@ -578,7 +595,7 @@ function conf_path($require_settings = TRUE, $reset = FALSE) {
 }
 
 /**
- * Set appropriate server variables needed for command line scripts to work.
+ * Sets appropriate server variables needed for command line scripts to work.
  *
  * This function can be called by command line scripts before bootstrapping
  * Drupal, to ensure that the page loads with the desired server parameters.
@@ -640,7 +657,7 @@ function drupal_override_server_variables($variables = array()) {
 }
 
 /**
- * Initialize PHP environment.
+ * Initializes the PHP environment.
  */
 function drupal_environment_initialize() {
   if (!isset($_SERVER['HTTP_REFERER'])) {
@@ -699,7 +716,7 @@ function drupal_environment_initialize() {
 }
 
 /**
- * Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
+ * Validates that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
  *
  * @return
  *  TRUE if only containing valid characters, or FALSE otherwise.
@@ -709,8 +726,7 @@ function drupal_valid_http_host($host) {
 }
 
 /**
- * Loads the configuration and sets the base URL, cookie domain, and
- * session name correctly.
+ * Sets the base URL, cookie domain, and session name from configuration.
  */
 function drupal_settings_initialize() {
   global $base_url, $base_path, $base_root;
@@ -796,9 +812,10 @@ function drupal_settings_initialize() {
 }
 
 /**
- * Returns and optionally sets the filename for a system item (module,
- * theme, etc.). The filename, whether provided, cached, or retrieved
- * from the database, is only returned if the file exists.
+ * Returns and optionally sets the filename for a system resource.
+ *
+ * The filename, whether provided, cached, or retrieved from the database, is
+ * only returned if the file exists.
  *
  * This function plays a key role in allowing Drupal's resources (modules
  * and themes) to be located in different places depending on a site's
@@ -828,6 +845,11 @@ function drupal_get_filename($type, $name, $filename = NULL) {
   // drupal_static().
   static $files = array(), $dirs = array();
 
+  // Profiles are a special case: they have a fixed location and naming.
+  if ($type == 'profile') {
+    $profile_filename = "profiles/$name/$name.profile";
+    $files[$type][$name] = file_exists($profile_filename) ? $profile_filename : FALSE;
+  }
   if (!isset($files[$type])) {
     $files[$type] = array();
   }
@@ -895,11 +917,11 @@ function drupal_get_filename($type, $name, $filename = NULL) {
 }
 
 /**
- * Load the persistent variable table.
+ * Loads the persistent variable table.
  *
  * The variable table is composed of values that have been saved in the table
- * with variable_set() as well as those explicitly specified in the configuration
- * file.
+ * with variable_set() as well as those explicitly specified in the
+ * configuration file.
  */
 function variable_initialize($conf = array()) {
   // NOTE: caching the variables improves performance by 20% when serving
@@ -1006,7 +1028,7 @@ function variable_del($name) {
 }
 
 /**
- * Retrieve the current page from the cache.
+ * Retrieves the current page from the cache.
  *
  * Note: we do not serve cached pages to authenticated users, or to anonymous
  * users when $_SESSION is non-empty. $_SESSION may contain status messages
@@ -1038,7 +1060,7 @@ function drupal_page_get_cache($check_only = FALSE) {
 }
 
 /**
- * Determine the cacheability of the current page.
+ * Determines the cacheability of the current page.
  *
  * @param $allow_caching
  *   Set to FALSE if you want to prevent this page to get cached.
@@ -1057,7 +1079,7 @@ function drupal_page_is_cacheable($allow_caching = NULL) {
 }
 
 /**
- * Invoke a bootstrap hook in all bootstrap modules that implement it.
+ * Invokes a bootstrap hook in all bootstrap modules that implement it.
  *
  * @param $hook
  *   The name of the bootstrap hook to invoke.
@@ -1079,8 +1101,9 @@ function bootstrap_invoke_all($hook) {
 }
 
 /**
- * Includes a file with the provided type and name. This prevents
- * including a theme, engine, module, etc., more than once.
+ * Includes a file with the provided type and name.
+ *
+ * This prevents including a theme, engine, module, etc., more than once.
  *
  * @param $type
  *   The type of item to load (i.e. theme, theme_engine, module).
@@ -1112,7 +1135,7 @@ function drupal_load($type, $name) {
 }
 
 /**
- * Set an HTTP response header for the current page.
+ * Sets an HTTP response header for the current page.
  *
  * Note: When sending a Content-Type header, always include a 'charset' type,
  * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
@@ -1148,11 +1171,12 @@ function drupal_add_http_header($name, $value, $append = FALSE) {
 }
 
 /**
- * Get the HTTP response headers for the current page.
+ * Gets the HTTP response headers for the current page.
  *
  * @param $name
  *   An HTTP header name. If omitted, all headers are returned as name/value
  *   pairs. If an array value is FALSE, the header has been unset.
+ *
  * @return
  *   A string containing the header value, or FALSE if the header has been set,
  *   or NULL if the header has not been set.
@@ -1169,6 +1193,8 @@ function drupal_get_http_header($name = NULL) {
 }
 
 /**
+ * Sets the preferred name for the HTTP header.
+ *
  * Header names are case-insensitive, but for maximum compatibility they should
  * follow "common form" (see RFC 2617, section 4.2).
  */
@@ -1182,9 +1208,10 @@ function _drupal_set_preferred_header_name($name = NULL) {
 }
 
 /**
- * Send the HTTP response headers previously set using drupal_add_http_header().
- * Add default headers, unless they have been replaced or unset using
- * drupal_add_http_header().
+ * Sends the HTTP response headers that were previously set, adding defaults.
+ *
+ * Headers are set in drupal_add_http_header(). Default headers are not set
+ * if they have been replaced or unset using drupal_add_http_header().
  *
  * @param $default_headers
  *   An array of headers as name/value pairs.
@@ -1219,7 +1246,7 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
 }
 
 /**
- * Set HTTP headers in preparation for a page response.
+ * Sets HTTP headers in preparation for a page response.
  *
  * Authenticated users are always given a 'no-cache' header, and will fetch a
  * fresh page on every request. This prevents authenticated users from seeing
@@ -1262,7 +1289,7 @@ function drupal_page_header() {
 }
 
 /**
- * Set HTTP headers in preparation for a cached page response.
+ * Sets HTTP headers in preparation for a cached page response.
  *
  * The headers allow as much as possible in proxies and browsers without any
  * particular knowledge about the pages. Modules can override these headers
@@ -1365,7 +1392,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
 }
 
 /**
- * Define the critical hooks that force modules to always be loaded.
+ * Defines the critical hooks that force modules to always be loaded.
  */
 function bootstrap_hooks() {
   return array('boot', 'exit', 'watchdog', 'language_init');
@@ -1418,10 +1445,10 @@ function drupal_unpack($obj, $field = 'data') {
  * $text = t("@name's blog", array('@name' => format_username($account)));
  * @endcode
  * Basically, you can put variables like @name into your string, and t() will
- * substitute their sanitized values at translation time (see $args below or
- * the Localization API pages referenced above for details). Translators can
- * then rearrange the string as necessary for the language (e.g., in Spanish,
- * it might be "blog de @name").
+ * substitute their sanitized values at translation time. (See the
+ * Localization API pages referenced above and the documentation of
+ * format_string() for details.) Translators can then rearrange the string as
+ * necessary for the language (e.g., in Spanish, it might be "blog de @name").
  *
  * During the Drupal installation phase, some resources used by t() wil not be
  * available to code that needs localization. See st() and get_t() for
@@ -1430,8 +1457,9 @@ function drupal_unpack($obj, $field = 'data') {
  * @param $string
  *   A string containing the English string to translate.
  * @param $args
- *   An associative array of replacements to make after translation.
- *   See format_string().
+ *   An associative array of replacements to make after translation. Based
+ *   on the first character of the key, the value is escaped and/or themed.
+ *   See format_string() for details.
  * @param $options
  *   An associative array of additional options, with the following elements:
  *   - 'langcode' (defaults to the current language): The language code to
@@ -1444,6 +1472,7 @@ function drupal_unpack($obj, $field = 'data') {
  *
  * @see st()
  * @see get_t()
+ * @see format_string()
  * @ingroup sanitization
  */
 function t($string, array $args = array(), array $options = array()) {
@@ -1482,7 +1511,7 @@ function t($string, array $args = array(), array $options = array()) {
 }
 
 /**
- * Replace placeholders with sanitized values in a string.
+ * Replaces placeholders with sanitized values in a string.
  *
  * @param $string
  *   A string containing placeholders.
@@ -1524,7 +1553,7 @@ function format_string($string, array $args = array()) {
 }
 
 /**
- * Encode special characters in a plain-text string for display as HTML.
+ * Encodes special characters in a plain-text string for display as HTML.
  *
  * Also validates strings as UTF-8 to prevent cross site scripting attacks on
  * Internet Explorer 6.
@@ -1563,6 +1592,7 @@ function check_plain($text) {
  *
  * @param $text
  *   The text to check.
+ *
  * @return
  *   TRUE if the text is valid UTF-8, FALSE if not.
  */
@@ -1604,7 +1634,7 @@ function request_uri() {
 }
 
 /**
- * Log an exception.
+ * Logs an exception.
  *
  * This is a wrapper function for watchdog() which automatically decodes an
  * exception.
@@ -1645,7 +1675,7 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
 }
 
 /**
- * Log a system message.
+ * Logs a system message.
  *
  * @param $type
  *   The category to which this message belongs. Can be any string, but the
@@ -1705,7 +1735,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
 }
 
 /**
- * Set a message which reflects the status of the performed operation.
+ * Sets a message which reflects the status of the performed operation.
  *
  * If the function is called with no arguments, this function returns all set
  * messages without clearing them.
@@ -1741,12 +1771,13 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
 }
 
 /**
- * Return all messages that have been set.
+ * Returns all messages that have been set.
  *
  * @param $type
  *   (optional) Only return messages of this type.
  * @param $clear_queue
  *   (optional) Set to FALSE if you do not want to clear the messages queue
+ *
  * @return
  *   An associative array, the key is the message type, the value an array
  *   of messages. If the $type parameter is passed, you get only that type,
@@ -1774,7 +1805,9 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
 }
 
 /**
- * Get the title of the current page, for display on the page and in the title bar.
+ * Gets the title of the current page.
+ *
+ * The title is displayed on the page and in the title bar.
  *
  * @return
  *   The current page's title.
@@ -1791,7 +1824,9 @@ function drupal_get_title() {
 }
 
 /**
- * Set the title of the current page, for display on the page and in the title bar.
+ * Sets the title of the current page.
+ *
+ * The title is displayed on the page and in the title bar.
  *
  * @param $title
  *   Optional string value to assign to the page title; or if set to NULL
@@ -1816,7 +1851,7 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
 }
 
 /**
- * Check to see if an IP address has been blocked.
+ * Checks to see if an IP address has been blocked.
  *
  * Blocked IP addresses are stored in the database by default. However for
  * performance reasons we allow an override in settings.php. This allows us
@@ -1825,6 +1860,7 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
  *
  * @param $ip
  *   IP address to check.
+ *
  * @return bool
  *   TRUE if access is denied, FALSE if access is allowed.
  */
@@ -1850,7 +1886,7 @@ function drupal_is_denied($ip) {
 }
 
 /**
- * Handle denied users.
+ * Handles denied users.
  *
  * @param $ip
  *   IP address to check. Prints a message and exits if access is denied.
@@ -1869,7 +1905,8 @@ function drupal_block_denied($ip) {
  *
  * This function is better than simply calling mt_rand() or any other built-in
  * PHP function because it can return a long string of bytes (compared to < 4
- * bytes normally from mt_rand()) and uses the best available pseudo-random source.
+ * bytes normally from mt_rand()) and uses the best available pseudo-random
+ * source.
  *
  * @param $count
  *   The number of characters (bytes) to return in the string.
@@ -1916,7 +1953,7 @@ function drupal_random_bytes($count)  {
 }
 
 /**
- * Calculate a base-64 encoded, URL-safe sha-256 hmac.
+ * Calculates a base-64 encoded, URL-safe sha-256 hmac.
  *
  * @param $data
  *   String to be validated with the hmac.
@@ -1934,7 +1971,7 @@ function drupal_hmac_base64($data, $key) {
 }
 
 /**
- * Calculate a base-64 encoded, URL-safe sha-256 hash.
+ * Calculates a base-64 encoded, URL-safe sha-256 hash.
  *
  * @param $data
  *   String to be hashed.
@@ -1977,7 +2014,8 @@ function drupal_hash_base64($data) {
  * @see drupal_array_merge_deep_array()
  */
 function drupal_array_merge_deep() {
-  return drupal_array_merge_deep_array(func_get_args());
+  $args = func_get_args();
+  return drupal_array_merge_deep_array($args);
 }
 
 /**
@@ -2038,20 +2076,22 @@ function drupal_anonymous_user() {
 }
 
 /**
- * A string describing a phase of Drupal to load. Each phase adds to the
- * previous one, so invoking a later phase automatically runs the earlier
- * phases too. The most important usage is that if you want to access the
- * Drupal database from a script without loading anything else, you can
- * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
+ * Ensures Drupal is bootstrapped to the specified phase.
+ *
+ * The bootstrap phase is an integer constant identifying a phase of Drupal
+ * to load. Each phase adds to the previous one, so invoking a later phase
+ * automatically runs the earlier phases as well. To access the Drupal
+ * database from a script without loading anything else, include bootstrap.inc
+ * and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
  *
  * @param $phase
  *   A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
  * @param $new_phase
  *   A boolean, set to FALSE if calling drupal_bootstrap from inside a
  *   function called from drupal_bootstrap (recursion).
+ *
  * @return
  *   The most recently completed phase.
- *
  */
 function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
   // Not drupal_static(), because does not depend on any run-time information.
@@ -2130,7 +2170,7 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
 }
 
 /**
- * Return the time zone of the current user.
+ * Returns the time zone of the current user.
  */
 function drupal_get_user_timezone() {
   global $user;
@@ -2145,7 +2185,7 @@ function drupal_get_user_timezone() {
 }
 
 /**
- * Custom PHP error handler.
+ * Provides custom PHP error handling.
  *
  * @param $error_level
  *   The level of the error raised.
@@ -2156,7 +2196,8 @@ function drupal_get_user_timezone() {
  * @param $line
  *   The line number the error was raised at.
  * @param $context
- *   An array that points to the active symbol table at the point the error occurred.
+ *   An array that points to the active symbol table at the point the error
+ *   occurred.
  */
 function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
   require_once DRUPAL_ROOT . '/includes/errors.inc';
@@ -2164,7 +2205,7 @@ function _drupal_error_handler($error_level, $message, $filename, $line, $contex
 }
 
 /**
- * Custom PHP exception handler.
+ * Provides custom PHP exception handling.
  *
  * Uncaught exceptions are those not enclosed in a try/catch block. They are
  * always fatal: the execution of the script will stop as soon as the exception
@@ -2192,7 +2233,7 @@ function _drupal_exception_handler($exception) {
 }
 
 /**
- * Bootstrap configuration: Setup script environment and load settings.php.
+ * Sets up the script environment and loads settings.php.
  */
 function _drupal_bootstrap_configuration() {
   // Set the Drupal custom error handler.
@@ -2207,7 +2248,7 @@ function _drupal_bootstrap_configuration() {
 }
 
 /**
- * Bootstrap page cache: Try to serve a page from cache.
+ * Attempts to serve a page from the cache.
  */
 function _drupal_bootstrap_page_cache() {
   global $user;
@@ -2263,7 +2304,7 @@ function _drupal_bootstrap_page_cache() {
 }
 
 /**
- * Bootstrap database: Initialize database system and register autoload functions.
+ * Initializes the database system and registers autoload functions.
  */
 function _drupal_bootstrap_database() {
   // Redirect the user to the installation script if Drupal has not been
@@ -2315,7 +2356,7 @@ function _drupal_bootstrap_database() {
 }
 
 /**
- * Bootstrap variables: Load system variables and all enabled bootstrap modules.
+ * Loads system variables and all enabled bootstrap modules.
  */
 function _drupal_bootstrap_variables() {
   global $conf;
@@ -2332,7 +2373,7 @@ function _drupal_bootstrap_variables() {
 }
 
 /**
- * Bootstrap page header: Invoke hook_boot(), initialize locking system, and send default HTTP headers.
+ * Invokes hook_boot(), initializes locking system, and sends HTTP headers.
  */
 function _drupal_bootstrap_page_header() {
   bootstrap_invoke_all('boot');
@@ -2355,8 +2396,7 @@ function drupal_get_bootstrap_phase() {
 }
 
 /**
- * Checks the current User-Agent string to see if this is an internal request
- * from SimpleTest. If so, returns the test prefix for this test.
+ * Returns the test prefix if this is an internal request from SimpleTest.
  *
  * @return
  *   Either the simpletest prefix (the string "simpletest" followed by any
@@ -2392,7 +2432,7 @@ function drupal_valid_test_ua() {
 }
 
 /**
- * Generate a user agent string with a HMAC and timestamp for simpletest.
+ * Generates a user agent string with a HMAC and timestamp for simpletest.
  */
 function drupal_generate_test_ua($prefix) {
   global $drupal_hash_salt;
@@ -2452,7 +2492,7 @@ function drupal_fast_404() {
 }
 
 /**
- * Return TRUE if a Drupal installation is currently being attempted.
+ * Returns TRUE if a Drupal installation is currently being attempted.
  */
 function drupal_installation_attempted() {
   return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install';
@@ -2495,10 +2535,9 @@ function get_t() {
 }
 
 /**
- * Initialize all the defined language types.
+ * Initializes all the defined language types.
  */
 function drupal_language_initialize() {
-  global $language;
   $types = language_types();
 
   // Ensure the language is correctly returned, even without multilanguage
@@ -2518,13 +2557,10 @@ function drupal_language_initialize() {
     // environments.
     bootstrap_invoke_all('language_init');
   }
-
-  // Send appropriate HTTP-Header for browsers and search engines.
-  header('Content-Language: ' . $language->language);
 }
 
 /**
- * The built-in language types.
+ * Returns a list of the built-in language types.
  *
  * @return
  *   An array of key-values pairs where the key is the language type and the
@@ -2539,7 +2575,7 @@ function drupal_language_types() {
 }
 
 /**
- * Return true if there is more than one language enabled.
+ * Returns TRUE if there is more than one language enabled.
  */
 function drupal_multilingual() {
   // The "language_count" variable stores the number of enabled languages to
@@ -2549,7 +2585,7 @@ function drupal_multilingual() {
 }
 
 /**
- * Return an array of the available language types.
+ * Returns an array of the available language types.
  */
 function language_types() {
   return array_keys(variable_get('language_types', drupal_language_types()));
@@ -2606,7 +2642,7 @@ function language_list($field = 'language') {
 }
 
 /**
- * Default language used on the site
+ * Returns the default language used on the site
  *
  * @param $property
  *   Optional property of the language object to return
@@ -2676,16 +2712,16 @@ function request_path() {
 }
 
 /**
- * Return a component of the current Drupal path.
+ * Returns a component of the current Drupal path.
  *
  * When viewing a page at the path "admin/structure/types", for example, arg(0)
  * returns "admin", arg(1) returns "structure", and arg(2) returns "types".
  *
- * Avoid use of this function where possible, as resulting code is hard to read.
- * In menu callback functions, attempt to use named arguments. See the explanation
- * in menu.inc for how to construct callbacks that take arguments. When attempting
- * to use this function to load an element from the current path, e.g. loading the
- * node on a node page, please use menu_get_object() instead.
+ * Avoid use of this function where possible, as resulting code is hard to
+ * read. In menu callback functions, attempt to use named arguments. See the
+ * explanation in menu.inc for how to construct callbacks that take arguments.
+ * When attempting to use this function to load an element from the current
+ * path, e.g. loading the node on a node page, use menu_get_object() instead.
  *
  * @param $index
  *   The index of the component, where each component is separated by a '/'
@@ -2725,6 +2761,8 @@ function arg($index = NULL, $path = NULL) {
 }
 
 /**
+ * Returns the IP address of the client machine.
+ *
  * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
  * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of
  * the proxy server, and not the client's. The actual header name can be
@@ -2774,7 +2812,7 @@ function ip_address() {
  */
 
 /**
- * Get the schema definition of a table, or the whole database schema.
+ * Gets the schema definition of a table, or the whole database schema.
  *
  * The returned schema will include any modifications made by any
  * module that implements hook_schema_alter().
@@ -2810,11 +2848,17 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
  */
 class SchemaCache extends DrupalCacheArray {
 
+  /**
+   * Constructs a SchemaCache object.
+   */
   public function __construct() {
     // Cache by request method.
     parent::__construct('schema:runtime:' . ($_SERVER['REQUEST_METHOD'] == 'GET'), 'cache');
   }
 
+  /**
+   * Overrides DrupalCacheArray::resolveCacheMiss().
+   */
   protected function resolveCacheMiss($offset) {
     $complete_schema = drupal_get_complete_schema();
     $value = isset($complete_schema[$offset]) ? $complete_schema[$offset] :  NULL;
@@ -2825,7 +2869,7 @@ class SchemaCache extends DrupalCacheArray {
 }
 
 /**
- * Get the whole database schema.
+ * Gets the whole database schema.
  *
  * The returned schema will include any modifications made by any
  * module that implements hook_schema_alter().
@@ -2895,13 +2939,14 @@ function drupal_get_complete_schema($rebuild = FALSE) {
  */
 
 /**
- * Confirm that an interface is available.
+ * Confirms that an interface is available.
  *
  * This function is rarely called directly. Instead, it is registered as an
  * spl_autoload()  handler, and PHP calls it for us when necessary.
  *
  * @param $interface
  *   The name of the interface to check or load.
+ *
  * @return
  *   TRUE if the interface is currently available, FALSE otherwise.
  */
@@ -2910,13 +2955,14 @@ function drupal_autoload_interface($interface) {
 }
 
 /**
- * Confirm that a class is available.
+ * Confirms that a class is available.
  *
  * This function is rarely called directly. Instead, it is registered as an
  * spl_autoload()  handler, and PHP calls it for us when necessary.
  *
  * @param $class
  *   The name of the class to check or load.
+ *
  * @return
  *   TRUE if the class is currently available, FALSE otherwise.
  */
@@ -2925,7 +2971,7 @@ function drupal_autoload_class($class) {
 }
 
 /**
- * Helper to check for a resource in the registry.
+ * Checks for a resource in the registry.
  *
  * @param $type
  *   The type of resource we are looking up, or one of the constants
@@ -2934,6 +2980,7 @@ function drupal_autoload_class($class) {
  * @param $name
  *   The name of the resource, or NULL if either of the REGISTRY_* constants
  *   is passed in.
+ *
  * @return
  *   TRUE if the resource was found, FALSE if not.
  *   NULL if either of the REGISTRY_* constants is passed in as $type.
@@ -3005,7 +3052,7 @@ function _registry_check_code($type, $name = NULL) {
 }
 
 /**
- * Rescan all enabled modules and rebuild the registry.
+ * Rescans all enabled modules and rebuilds the registry.
  *
  * Rescans all code in modules or includes directories, storing the location of
  * each interface or class in the database.
@@ -3016,7 +3063,7 @@ function registry_rebuild() {
 }
 
 /**
- * Update the registry based on the latest files listed in the database.
+ * Updates the registry based on the latest files listed in the database.
  *
  * This function should be used when system_rebuild_module_data() does not need
  * to be called, because it is already known that the list of files in the
@@ -3034,7 +3081,7 @@ function registry_update() {
  */
 
 /**
- * Central static variable storage.
+ * Provides central static variable storage.
  *
  * All functions requiring a static variable to persist or cache data within
  * a single page request are encouraged to use this function unless it is
@@ -3185,7 +3232,7 @@ function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
 }
 
 /**
- * Reset one or all centrally stored static variable(s).
+ * Resets one or all centrally stored static variable(s).
  *
  * @param $name
  *   Name of the static variable to reset. Omit to reset all variables.
@@ -3195,7 +3242,7 @@ function drupal_static_reset($name = NULL) {
 }
 
 /**
- * Detect whether the current script is running in a command-line environment.
+ * Detects whether the current script is running in a command-line environment.
  */
 function drupal_is_cli() {
   return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)));
@@ -3203,7 +3250,8 @@ function drupal_is_cli() {
 
 /**
  * Formats text for emphasized display in a placeholder inside a sentence.
- * Used automatically by t().
+ *
+ * Used automatically by format_string().
  *
  * @param $text
  *   The text to format (plain-text).
@@ -3216,7 +3264,7 @@ function drupal_placeholder($text) {
 }
 
 /**
- * Register a function for execution on shutdown.
+ * Registers a function for execution on shutdown.
  *
  * Wrapper for register_shutdown_function() that catches thrown exceptions to
  * avoid "Exception thrown without a stack frame in Unknown".
@@ -3251,7 +3299,7 @@ function &drupal_register_shutdown_function($callback = NULL) {
 }
 
 /**
- * Internal function used to execute registered shutdown functions.
+ * Executes registered shutdown functions.
  */
 function _drupal_shutdown_function() {
   $callbacks = &drupal_register_shutdown_function();
index d9bb0f9..9e0dd01 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 /**
- * A stub cache implementation to be used during the installation process.
+ * Defines a stub cache implementation to be used during installation.
  *
  * The stub implementation is needed when database access is not yet available.
  * Because Drupal's caching system never requires that cached data be present,
  * normal operations would have a negative impact on performance.
  */
 class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterface {
+
+  /**
+   * Overrides DrupalDatabaseCache::get().
+   */
   function get($cid) {
     return FALSE;
   }
 
+  /**
+   * Overrides DrupalDatabaseCache::getMultiple().
+   */
   function getMultiple(&$cids) {
     return array();
   }
 
+  /**
+   * Overrides DrupalDatabaseCache::set().
+   */
   function set($cid, $data, $expire = CACHE_PERMANENT) {
   }
 
+  /**
+   * Overrides DrupalDatabaseCache::clear().
+   */
   function clear($cid = NULL, $wildcard = FALSE) {
     // If there is a database cache, attempt to clear it whenever possible. The
     // reason for doing this is that the database cache can accumulate data
@@ -52,6 +65,9 @@ class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterfac
     }
   }
 
+  /**
+   * Overrides DrupalDatabaseCache::isEmpty().
+   */
   function isEmpty() {
     return TRUE;
   }
index 8666874..eb7f090 100644 (file)
@@ -1,18 +1,23 @@
 <?php
 
 /**
- * Get the cache object for a cache bin.
+ * @file
+ * Functions and interfaces for cache handling.
+ */
+
+/**
+ * Gets the cache object for a cache bin.
  *
  * By default, this returns an instance of the DrupalDatabaseCache class.
  * Classes implementing DrupalCacheInterface can register themselves both as a
  * default implementation and for specific bins.
  *
- * @see DrupalCacheInterface
- *
  * @param $bin
  *   The cache bin for which the cache object should be returned.
  * @return DrupalCacheInterface
  *   The cache object associated with the specified bin.
+ *
+ * @see DrupalCacheInterface
  */
 function _cache_get_object($bin) {
   // We do not use drupal_static() here because we do not want to change the
@@ -29,7 +34,7 @@ function _cache_get_object($bin) {
 }
 
 /**
- * Return data from the persistent cache
+ * Returns data from the persistent cache.
  *
  * Data may be stored as either plain text or as serialized data. cache_get
  * will automatically return unserialized objects and arrays.
@@ -44,19 +49,22 @@ function _cache_get_object($bin) {
  *
  * @return
  *   The cache or FALSE on failure.
+ *
+ * @see cache_set()
  */
 function cache_get($cid, $bin = 'cache') {
   return _cache_get_object($bin)->get($cid);
 }
 
 /**
- * Return data from the persistent cache when given an array of cache IDs.
+ * Returns data from the persistent cache when given an array of cache IDs.
  *
  * @param $cids
  *   An array of cache IDs for the data to retrieve. This is passed by
  *   reference, and will have the IDs successfully returned from cache removed.
  * @param $bin
  *   The cache bin where the data is stored.
+ *
  * @return
  *   An array of the items successfully returned from cache indexed by cid.
  */
@@ -65,7 +73,7 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
 }
 
 /**
- * Store data in the persistent cache.
+ * Stores data in the persistent cache.
  *
  * The persistent cache is split up into several cache bins. In the default
  * cache implementation, each cache bin corresponds to a database table by the
@@ -132,13 +140,15 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
  *     general cache wipe.
  *   - A Unix timestamp: Indicates that the item should be kept at least until
  *     the given time, after which it behaves like CACHE_TEMPORARY.
+ *
+ * @see cache_get()
  */
 function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
   return _cache_get_object($bin)->set($cid, $data, $expire);
 }
 
 /**
- * Expire data from the cache.
+ * Expires data from the cache.
  *
  * If called without arguments, expirable entries will be cleared from the
  * cache_page and cache_block bins.
@@ -146,15 +156,12 @@ function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
  * @param $cid
  *   If set, the cache ID to delete. Otherwise, all cache entries that can
  *   expire are deleted.
- *
  * @param $bin
- *   If set, the bin $bin to delete from. Mandatory
- *   argument if $cid is set.
- *
+ *   If set, the cache bin to delete from. Mandatory argument if $cid is set.
  * @param $wildcard
- *   If $wildcard is TRUE, cache IDs starting with $cid are deleted in
- *   addition to the exact cache ID specified by $cid.  If $wildcard is
- *   TRUE and $cid is '*' then the entire bin $bin is emptied.
+ *   If TRUE, cache IDs starting with $cid are deleted in addition to the
+ *   exact cache ID specified by $cid. If $wildcard is TRUE and $cid is '*',
+ *   the entire cache bin is emptied.
  */
 function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
   if (!isset($cid) && !isset($bin)) {
@@ -170,13 +177,14 @@ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
 }
 
 /**
- * Check if a cache bin is empty.
+ * Checks if a cache bin is empty.
  *
  * A cache bin is considered empty if it does not contain any valid data for any
  * cache ID.
  *
  * @param $bin
  *   The cache bin to check.
+ *
  * @return
  *   TRUE if the cache bin specified is empty.
  */
@@ -185,7 +193,7 @@ function cache_is_empty($bin) {
 }
 
 /**
- * Interface for cache implementations.
+ * Defines an interface for cache implementations.
  *
  * All cache implementations have to implement this interface.
  * DrupalDatabaseCache provides the default implementation, which can be
@@ -223,7 +231,7 @@ function cache_is_empty($bin) {
  */
 interface DrupalCacheInterface {
   /**
-   * Constructor.
+   * Constructs a new cache interface.
    *
    * @param $bin
    *   The cache bin for which the object is created.
@@ -231,31 +239,34 @@ interface DrupalCacheInterface {
   function __construct($bin);
 
   /**
-   * Return data from the persistent cache. Data may be stored as either plain
-   * text or as serialized data. cache_get will automatically return
-   * unserialized objects and arrays.
+   * Returns data from the persistent cache.
+   *
+   * Data may be stored as either plain text or as serialized data. cache_get()
+   * will automatically return unserialized objects and arrays.
    *
    * @param $cid
    *   The cache ID of the data to retrieve.
+   *
    * @return
    *   The cache or FALSE on failure.
    */
   function get($cid);
 
   /**
-   * Return data from the persistent cache when given an array of cache IDs.
+   * Returns data from the persistent cache when given an array of cache IDs.
    *
    * @param $cids
    *   An array of cache IDs for the data to retrieve. This is passed by
    *   reference, and will have the IDs successfully returned from cache
    *   removed.
+   *
    * @return
    *   An array of the items successfully returned from cache indexed by cid.
    */
    function getMultiple(&$cids);
 
   /**
-   * Store data in the persistent cache.
+   * Stores data in the persistent cache.
    *
    * @param $cid
    *   The cache ID of the data to store.
@@ -276,8 +287,10 @@ interface DrupalCacheInterface {
 
 
   /**
-   * Expire data from the cache. If called without arguments, expirable
-   * entries will be cleared from the cache_page and cache_block bins.
+   * Expires data from the cache.
+   *
+   * If called without arguments, expirable entries will be cleared from the
+   * cache_page and cache_block bins.
    *
    * @param $cid
    *   If set, the cache ID to delete. Otherwise, all cache entries that can
@@ -290,7 +303,7 @@ interface DrupalCacheInterface {
   function clear($cid = NULL, $wildcard = FALSE);
 
   /**
-   * Check if a cache bin is empty.
+   * Checks if a cache bin is empty.
    *
    * A cache bin is considered empty if it does not contain any valid data for
    * any cache ID.
@@ -302,7 +315,7 @@ interface DrupalCacheInterface {
 }
 
 /**
- * Default cache implementation.
+ * Defines a default cache implementation.
  *
  * This is Drupal's default cache implementation. It uses the database to store
  * cached data. Each cache bin corresponds to a database table by the same name.
@@ -310,16 +323,25 @@ interface DrupalCacheInterface {
 class DrupalDatabaseCache implements DrupalCacheInterface {
   protected $bin;
 
+  /**
+   * Constructs a new DrupalDatabaseCache object.
+   */
   function __construct($bin) {
     $this->bin = $bin;
   }
 
+  /**
+   * Implements DrupalCacheInterface::get().
+   */
   function get($cid) {
     $cids = array($cid);
     $cache = $this->getMultiple($cids);
     return reset($cache);
   }
 
+  /**
+   * Implements DrupalCacheInterface::getMultiple().
+   */
   function getMultiple(&$cids) {
     try {
       // Garbage collection necessary when enforcing a minimum cache lifetime.
@@ -373,13 +395,14 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
   }
 
   /**
-   * Prepare a cached item.
+   * Prepares a cached item.
    *
    * Checks that items are either permanent or did not expire, and unserializes
    * data as appropriate.
    *
    * @param $cache
    *   An item loaded from cache_get() or cache_get_multiple().
+   *
    * @return
    *   The item with data unserialized as appropriate or FALSE if there is no
    *   valid item to load.
@@ -408,6 +431,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
     return $cache;
   }
 
+  /**
+   * Implements DrupalCacheInterface::set().
+   */
   function set($cid, $data, $expire = CACHE_PERMANENT) {
     $fields = array(
       'serialized' => 0,
@@ -434,6 +460,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
     }
   }
 
+  /**
+   * Implements DrupalCacheInterface::clear().
+   */
   function clear($cid = NULL, $wildcard = FALSE) {
     global $user;
 
@@ -496,6 +525,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
     }
   }
 
+  /**
+   * Implements DrupalCacheInterface::isEmpty().
+   */
   function isEmpty() {
     $this->garbageCollection();
     $query = db_select($this->bin);
index 3a3d274..43e2118 100644 (file)
@@ -70,8 +70,7 @@ define('CSS_DEFAULT', 0);
 define('CSS_THEME', 100);
 
 /**
- * The default group for JavaScript libraries, settings or jQuery plugins added
- * to the page.
+ * The default group for JavaScript and jQuery libraries added to the page.
  */
 define('JS_LIBRARY', -100);
 
@@ -86,8 +85,9 @@ define('JS_DEFAULT', 0);
 define('JS_THEME', 100);
 
 /**
- * Error code indicating that the request made by drupal_http_request() exceeded
- * the specified timeout.
+ * Error code indicating that the request exceeded the specified timeout.
+ *
+ * @see drupal_http_request()
  */
 define('HTTP_REQUEST_TIMEOUT', -1);
 
@@ -110,31 +110,36 @@ define('HTTP_REQUEST_TIMEOUT', -1);
  */
 
 /**
- * The block should not get cached. This setting should be used:
- * - for simple blocks (notably those that do not perform any db query),
- * where querying the db cache would be more expensive than directly generating
- * the content.
- * - for blocks that change too frequently.
+ * The block should not get cached.
+ *
+ * This setting should be used:
+ * - For simple blocks (notably those that do not perform any db query), where
+ *   querying the db cache would be more expensive than directly generating the
+ *   content.
+ * - For blocks that change too frequently.
  */
 define('DRUPAL_NO_CACHE', -1);
 
 /**
- * The block is handling its own caching in its hook_block_view(). From the
- * perspective of the block cache system, this is equivalent to DRUPAL_NO_CACHE.
- * Useful when time based expiration is needed or a site uses a node access
- * which invalidates standard block cache.
+ * The block is handling its own caching in its hook_block_view().
+ *
+ * From the perspective of the block cache system, this is equivalent to
+ * DRUPAL_NO_CACHE. Useful when time based expiration is needed or a site uses
+ * a node access which invalidates standard block cache.
  */
 define('DRUPAL_CACHE_CUSTOM', -2);
 
 /**
- * The block or element can change depending on the roles the user viewing the
- * page belongs to. This is the default setting for blocks, used when the block
- * does not specify anything.
+ * The block or element can change depending on the user's roles.
+ *
+ * This is the default setting for blocks, used when the block does not specify
+ * anything.
  */
 define('DRUPAL_CACHE_PER_ROLE', 0x0001);
 
 /**
- * The block or element can change depending on the user viewing the page.
+ * The block or element can change depending on the user.
+ *
  * This setting can be resource-consuming for sites with large number of users,
  * and thus should only be used when DRUPAL_CACHE_PER_ROLE is not sufficient.
  */
@@ -146,12 +151,12 @@ define('DRUPAL_CACHE_PER_USER', 0x0002);
 define('DRUPAL_CACHE_PER_PAGE', 0x0004);
 
 /**
- * The block or element is the same for every user on every page where it is visible.
+ * The block or element is the same for every user and page that it is visible.
  */
 define('DRUPAL_CACHE_GLOBAL', 0x0008);
 
 /**
- * Add content to a specified region.
+ * Adds content to a specified region.
  *
  * @param $region
  *   Page region the content is added to.
@@ -168,7 +173,7 @@ function drupal_add_region_content($region = NULL, $data = NULL) {
 }
 
 /**
- * Get assigned content for a given region.
+ * Gets assigned content for a given region.
  *
  * @param $region
  *   A specified region to fetch content for. If NULL, all regions will be
@@ -194,13 +199,13 @@ function drupal_get_region_content($region = NULL, $delimiter = ' ') {
 }
 
 /**
- * Get the name of the currently active install profile.
+ * Gets the name of the currently active install profile.
  *
  * When this function is called during Drupal's initial installation process,
  * the name of the profile that's about to be installed is stored in the global
  * installation state. At all other times, the standard Drupal systems variable
- * table contains the name of the current profile, and we can call variable_get()
- * to determine what one is active.
+ * table contains the name of the current profile, and we can call
+ * variable_get() to determine what one is active.
  *
  * @return $profile
  *   The name of the install profile.
@@ -220,7 +225,7 @@ function drupal_get_profile() {
 
 
 /**
- * Set the breadcrumb trail for the current page.
+ * Sets the breadcrumb trail for the current page.
  *
  * @param $breadcrumb
  *   Array of links, starting with "home" and proceeding up to but not including
@@ -236,7 +241,7 @@ function drupal_set_breadcrumb($breadcrumb = NULL) {
 }
 
 /**
- * Get the breadcrumb trail for the current page.
+ * Gets the breadcrumb trail for the current page.
  */
 function drupal_get_breadcrumb() {
   $breadcrumb = drupal_set_breadcrumb();
@@ -265,7 +270,7 @@ function drupal_get_rdf_namespaces() {
 }
 
 /**
- * Add output to the head tag of the HTML page.
+ * Adds output to the HEAD tag of the HTML page.
  *
  * This function can be called as long the headers aren't sent. Pass no
  * arguments (or NULL for both) to retrieve the currently stored elements.
@@ -333,7 +338,7 @@ function _drupal_default_html_head() {
 }
 
 /**
- * Retrieve output to be displayed in the HEAD tag of the HTML page.
+ * Retrieves output to be displayed in the HEAD tag of the HTML page.
  */
 function drupal_get_html_head() {
   $elements = drupal_add_html_head();
@@ -342,7 +347,7 @@ function drupal_get_html_head() {
 }
 
 /**
- * Add a feed URL for the current page.
+ * Adds a feed URL for the current page.
  *
  * This function can be called as long the HTML header hasn't been sent.
  *
@@ -370,7 +375,7 @@ function drupal_add_feed($url = NULL, $title = '') {
 }
 
 /**
- * Get the feed URLs for the current page.
+ * Gets the feed URLs for the current page.
  *
  * @param $delimiter
  *   A delimiter to split feeds by.
@@ -387,7 +392,7 @@ function drupal_get_feeds($delimiter = "\n") {
  */
 
 /**
- * Process a URL query parameter array to remove unwanted elements.
+ * Processes a URL query parameter array to remove unwanted elements.
  *
  * @param $query
  *   (optional) An array to be processed. Defaults to $_GET.
@@ -432,7 +437,7 @@ function drupal_get_query_parameters(array $query = NULL, array $exclude = array
 }
 
 /**
- * Split an URL-encoded query string into an array.
+ * Splits a URL-encoded query string into an array.
  *
  * @param $query
  *   The query string to split.
@@ -452,7 +457,7 @@ function drupal_get_query_array($query) {
 }
 
 /**
- * Parse an array into a valid, rawurlencoded query string.
+ * Parses an array into a valid, rawurlencoded query string.
  *
  * This differs from http_build_query() as we need to rawurlencode() (instead of
  * urlencode()) all query parameters.
@@ -493,7 +498,7 @@ function drupal_http_build_query(array $query, $parent = '') {
 }
 
 /**
- * Prepare a 'destination' URL query parameter for use in combination with drupal_goto().
+ * Prepares a 'destination' URL query parameter for use with drupal_goto().
  *
  * Used to direct the user back to the referring page after completing a form.
  * By default the current URL is returned. If a destination exists in the
@@ -524,7 +529,7 @@ function drupal_get_destination() {
 }
 
 /**
- * Wrapper around parse_url() to parse a system URL string into an associative array, suitable for url().
+ * Parses a system URL string into an associative array suitable for url().
  *
  * This function should only be used for URLs that have been generated by the
  * system, resp. url(). It should not be used for URLs that come from external
@@ -621,7 +626,7 @@ function drupal_encode_path($path) {
 }
 
 /**
- * Send the user to a different Drupal page.
+ * Sends the user to a different Drupal page.
  *
  * This issues an on-site HTTP redirect. The function makes sure the redirected
  * URL is formatted correctly.
@@ -686,7 +691,7 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
 }
 
 /**
- * Deliver a "site is under maintenance" message to the browser.
+ * Delivers a "site is under maintenance" message to the browser.
  *
  * Page callback functions wanting to report a "site offline" message should
  * return MENU_SITE_OFFLINE instead of calling drupal_site_offline(). However,
@@ -698,7 +703,7 @@ function drupal_site_offline() {
 }
 
 /**
- * Deliver a "page not found" error to the browser.
+ * Delivers a "page not found" error to the browser.
  *
  * Page callback functions wanting to report a "page not found" message should
  * return MENU_NOT_FOUND instead of calling drupal_not_found(). However,
@@ -710,19 +715,20 @@ function drupal_not_found() {
 }
 
 /**
- * Deliver a "access denied" error to the browser.
+ * Delivers an "access denied" error to the browser.
  *
  * Page callback functions wanting to report an "access denied" message should
  * return MENU_ACCESS_DENIED instead of calling drupal_access_denied(). However,
  * functions that are invoked in contexts where that return value might not
- * bubble up to menu_execute_active_handler() should call drupal_access_denied().
+ * bubble up to menu_execute_active_handler() should call
+ * drupal_access_denied().
  */
 function drupal_access_denied() {
   drupal_deliver_page(MENU_ACCESS_DENIED);
 }
 
 /**
- * Perform an HTTP request.
+ * Performs an HTTP request.
  *
  * This is a flexible and powerful HTTP client implementation. Correctly
  * handles GET, POST, PUT or any other HTTP requests. Handles redirects.
@@ -832,7 +838,7 @@ function drupal_http_request($url, array $options = array()) {
     // Mark that this request failed. This will trigger a check of the web
     // server's ability to make outgoing HTTP requests the next time that
     // requirements checking is performed.
-    // See system_requirements()
+    // See system_requirements().
     variable_set('drupal_http_request_fails', TRUE);
 
     return $result;
@@ -1023,6 +1029,14 @@ function drupal_http_request($url, array $options = array()) {
  * @} End of "HTTP handling".
  */
 
+/**
+ * Strips slashes from a string or array of strings.
+ *
+ * Callback for array_walk() within fix_gpx_magic().
+ *
+ * @param $item
+ *   An individual string or array of strings from superglobals.
+ */
 function _fix_gpc_magic(&$item) {
   if (is_array($item)) {
     array_walk($item, '_fix_gpc_magic');
@@ -1033,11 +1047,19 @@ function _fix_gpc_magic(&$item) {
 }
 
 /**
- * Helper function to strip slashes from $_FILES skipping over the tmp_name keys
- * since PHP generates single backslashes for file paths on Windows systems.
+ * Strips slashes from $_FILES items.
+ *
+ * Callback for array_walk() within fix_gpc_magic().
  *
- * tmp_name does not have backslashes added see
- * http://php.net/manual/en/features.file-upload.php#42280
+ * The tmp_name key is skipped keys since PHP generates single backslashes for
+ * file paths on Windows systems.
+ *
+ * @param $item
+ *   An item from $_FILES.
+ * @param $key
+ *   The key for the item within $_FILES.
+ *
+ * @see http://php.net/manual/en/features.file-upload.php#42280
  */
 function _fix_gpc_magic_files(&$item, $key) {
   if ($key != 'tmp_name') {
@@ -1051,7 +1073,10 @@ function _fix_gpc_magic_files(&$item, $key) {
 }
 
 /**
- * Fix double-escaping problems caused by "magic quotes" in some PHP installations.
+ * Fixes double-escaping caused by "magic quotes" in some PHP installations.
+ *
+ * @see _fix_gpc_magic()
+ * @see _fix_gpc_magic_files()
  */
 function fix_gpc_magic() {
   static $fixed = FALSE;
@@ -1072,12 +1097,13 @@ function fix_gpc_magic() {
  */
 
 /**
- * Verify the syntax of the given e-mail address.
+ * Verifies the syntax of the given e-mail address.
  *
  * Empty e-mail addresses are allowed. See RFC 2822 for details.
  *
  * @param $mail
  *   A string containing an e-mail address.
+ *
  * @return
  *   TRUE if the address is in a valid format.
  */
@@ -1086,7 +1112,7 @@ function valid_email_address($mail) {
 }
 
 /**
- * Verify the syntax of the given URL.
+ * Verifies the syntax of the given URL.
  *
  * This function should only be used on actual URLs. It should not be used for
  * Drupal menu paths, which can contain arbitrary characters.
@@ -1095,6 +1121,7 @@ function valid_email_address($mail) {
  *   The URL to verify.
  * @param $absolute
  *   Whether the URL is absolute (beginning with a scheme such as "http:").
+ *
  * @return
  *   TRUE if the URL is in a valid format.
  */
@@ -1127,7 +1154,7 @@ function valid_url($url, $absolute = FALSE) {
  */
 
 /**
- * Register an event for the current visitor to the flood control mechanism.
+ * Registers an event for the current visitor to the flood control mechanism.
  *
  * @param $name
  *   The name of an event.
@@ -1154,7 +1181,7 @@ function flood_register_event($name, $window = 3600, $identifier = NULL) {
 }
 
 /**
- * Make the flood control mechanism forget about an event for the current visitor.
+ * Makes the flood control mechanism forget an event for the current visitor.
  *
  * @param $name
  *   The name of an event.
@@ -1172,7 +1199,7 @@ function flood_clear_event($name, $identifier = NULL) {
 }
 
 /**
- * Checks whether user is allowed to proceed with the specified event.
+ * Checks whether a user is allowed to proceed with the specified event.
  *
  * Events can have thresholds saying that each user can only do that event
  * a certain number of times in a time window. This function verifies that the
@@ -1266,7 +1293,7 @@ function drupal_strip_dangerous_protocols($uri) {
 }
 
 /**
- * Strips dangerous protocols (e.g. 'javascript:') from a URI and encodes it for output to an HTML attribute value.
+ * Strips dangerous protocols from a URI and encodes it for output to HTML.
  *
  * @param $uri
  *   A plain-text URI that might contain dangerous protocols.
@@ -1286,7 +1313,7 @@ function check_url($uri) {
 }
 
 /**
- * Very permissive XSS/HTML filter for admin-only use.
+ * Applies a very permissive XSS/HTML filter for admin-only use.
  *
  * Use only for fields where it is impractical to use the
  * whole filter system, but where some (mainly inline) mark-up
@@ -1300,7 +1327,7 @@ function filter_xss_admin($string) {
 }
 
 /**
- * Filters an HTML string to prevent cross-site-scripting (XSS) vulnerabilities.
+ * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
  *
  * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
  * For examples of various XSS attacks, see: http://ha.ckers.org/xss.html.
@@ -1331,21 +1358,21 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
   if (!drupal_validate_utf8($string)) {
     return '';
   }
-  // Store the text format
+  // Store the text format.
   _filter_xss_split($allowed_tags, TRUE);
-  // Remove NULL characters (ignored by some browsers)
+  // Remove NULL characters (ignored by some browsers).
   $string = str_replace(chr(0), '', $string);
-  // Remove Netscape 4 JS entities
+  // Remove Netscape 4 JS entities.
   $string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
 
-  // Defuse all HTML entities
+  // Defuse all HTML entities.
   $string = str_replace('&', '&amp;', $string);
-  // Change back only well-formed entities in our whitelist
-  // Decimal numeric entities
+  // Change back only well-formed entities in our whitelist:
+  // Decimal numeric entities.
   $string = preg_replace('/&amp;#([0-9]+;)/', '&#\1', $string);
-  // Hexadecimal numeric entities
+  // Hexadecimal numeric entities.
   $string = preg_replace('/&amp;#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $string);
-  // Named entities
+  // Named entities.
   $string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);
 
   return preg_replace_callback('%
@@ -1369,6 +1396,7 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
  *   If $store is FALSE then the array has one element, the HTML tag to process.
  * @param $store
  *   Whether to store $m.
+ *
  * @return
  *   If the element isn't allowed, an empty string. Otherwise, the cleaned up
  *   version of the HTML element.
@@ -1384,16 +1412,16 @@ function _filter_xss_split($m, $store = FALSE) {
   $string = $m[1];
 
   if (substr($string, 0, 1) != '<') {
-    // We matched a lone ">" character
+    // We matched a lone ">" character.
     return '&gt;';
   }
   elseif (strlen($string) == 1) {
-    // We matched a lone "<" character
+    // We matched a lone "<" character.
     return '&lt;';
   }
 
   if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
-    // Seriously malformed
+    // Seriously malformed.
     return '';
   }
 
@@ -1407,7 +1435,7 @@ function _filter_xss_split($m, $store = FALSE) {
   }
 
   if (!isset($allowed_html[strtolower($elem)])) {
-    // Disallowed HTML element
+    // Disallowed HTML element.
     return '';
   }
 
@@ -1423,7 +1451,7 @@ function _filter_xss_split($m, $store = FALSE) {
   $attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist, -1, $count);
   $xhtml_slash = $count ? ' /' : '';
 
-  // Clean up attributes
+  // Clean up attributes.
   $attr2 = implode(' ', _filter_xss_attributes($attrlist));
   $attr2 = preg_replace('/[<>]/', '', $attr2);
   $attr2 = strlen($attr2) ? ' ' . $attr2 : '';
@@ -1448,7 +1476,7 @@ function _filter_xss_attributes($attr) {
 
     switch ($mode) {
       case 0:
-        // Attribute name, href for instance
+        // Attribute name, href for instance.
         if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
           $attrname = strtolower($match[1]);
           $skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
@@ -1458,7 +1486,7 @@ function _filter_xss_attributes($attr) {
         break;
 
       case 1:
-        // Equals sign or valueless ("selected")
+        // Equals sign or valueless ("selected").
         if (preg_match('/^\s*=\s*/', $attr)) {
           $working = 1; $mode = 2;
           $attr = preg_replace('/^\s*=\s*/', '', $attr);
@@ -1475,7 +1503,7 @@ function _filter_xss_attributes($attr) {
         break;
 
       case 2:
-        // Attribute value, a URL after href= for instance
+        // Attribute value, a URL after href= for instance.
         if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
           $thisval = filter_xss_bad_protocol($match[1]);
 
@@ -1512,7 +1540,7 @@ function _filter_xss_attributes($attr) {
     }
 
     if ($working == 0) {
-      // not well formed, remove and try again
+      // Not well formed; remove and try again.
       $attr = preg_replace('/
         ^
         (
@@ -1536,15 +1564,16 @@ function _filter_xss_attributes($attr) {
 }
 
 /**
- * Processes an HTML attribute value and ensures it does not contain an URL with a disallowed protocol (e.g. javascript:).
+ * Processes an HTML attribute value and strips dangerous protocols from URLs.
  *
  * @param $string
  *   The string with the attribute value.
  * @param $decode
- *   (Deprecated) Whether to decode entities in the $string. Set to FALSE if the
+ *   (deprecated) Whether to decode entities in the $string. Set to FALSE if the
  *   $string is in plain text, TRUE otherwise. Defaults to TRUE. This parameter
  *   is deprecated and will be removed in Drupal 8. To process a plain-text URI,
  *   call drupal_strip_dangerous_protocols() or check_url() instead.
+ *
  * @return
  *   Cleaned up and HTML-escaped version of $string.
  */
@@ -1598,7 +1627,7 @@ function format_rss_channel($title, $link, $description, $items, $langcode = NUL
 }
 
 /**
- * Format a single RSS item.
+ * Formats a single RSS item.
  *
  * Arbitrary elements may be added using the $args associative array.
  */
@@ -1614,7 +1643,7 @@ function format_rss_item($title, $link, $description, $args = array()) {
 }
 
 /**
- * Format XML elements.
+ * Formats XML elements.
  *
  * @param $array
  *   An array where each item represents an element and is either a:
@@ -1653,7 +1682,7 @@ function format_xml_elements($array) {
 }
 
 /**
- * Format a string containing a count of items.
+ * Formats a string containing a count of items.
  *
  * This function ensures that the string is pluralized correctly. Since t() is
  * called by this function, make sure not to pass already-localized strings to
@@ -1675,31 +1704,27 @@ function format_xml_elements($array) {
  * @param $count
  *   The item count to display.
  * @param $singular
- *   The string for the singular case. Please make sure it is clear this is
- *   singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
- *   Do not use @count in the singular string.
+ *   The string for the singular case. Make sure it is clear this is singular,
+ *   to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
+ *   use @count in the singular string.
  * @param $plural
- *   The string for the plural case. Please make sure it is clear this is plural,
- *   to ease translation. Use @count in place of the item count, as in "@count
- *   new comments".
+ *   The string for the plural case. Make sure it is clear this is plural, to
+ *   ease translation. Use @count in place of the item count, as in
+ *   "@count new comments".
  * @param $args
- *   An associative array of replacements to make after translation. Incidences
+ *   An associative array of replacements to make after translation. Instances
  *   of any key in this array are replaced with the corresponding value.
- *   Based on the first character of the key, the value is escaped and/or themed:
- *    - !variable: inserted as is
- *    - @variable: escape plain text to HTML (check_plain)
- *    - %variable: escape text and theme as a placeholder for user-submitted
- *      content (check_plain + drupal_placeholder)
- *   Note that you do not need to include @count in this array.
- *   This replacement is done automatically for the plural case.
+ *   Based on the first character of the key, the value is escaped and/or
+ *   themed. See format_string(). Note that you do not need to include @count
+ *   in this array; this replacement is done automatically for the plural case.
  * @param $options
- *   An associative array of additional options, with the following keys:
- *     - 'langcode' (default to the current language) The language code to
- *       translate to a language other than what is used to display the page.
- *     - 'context' (default to the empty context) The context the source string
- *       belongs to.
+ *   An associative array of additional options. See t() for allowed keys.
+ *
  * @return
  *   A translated string.
+ *
+ * @see t()
+ * @see format_string()
  */
 function format_plural($count, $singular, $plural, array $args = array(), array $options = array()) {
   $args['@count'] = $count;
@@ -1728,11 +1753,12 @@ function format_plural($count, $singular, $plural, array $args = array(), array
 }
 
 /**
- * Parse a given byte count.
+ * Parses a given byte count.
  *
  * @param $size
  *   A size expressed as a number of bytes with optional SI or IEC binary unit
  *   prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+ *
  * @return
  *   An integer representation of the size in bytes.
  */
@@ -1749,13 +1775,14 @@ function parse_size($size) {
 }
 
 /**
- * Generate a string representation for the given byte count.
+ * Generates a string representation for the given byte count.
  *
  * @param $size
  *   A size in bytes.
  * @param $langcode
  *   Optional language code to translate to a language other than what is used
  *   to display the page.
+ *
  * @return
  *   A translated string representation of the size.
  */
@@ -1788,19 +1815,20 @@ function format_size($size, $langcode = NULL) {
 }
 
 /**
- * Format a time interval with the requested granularity.
+ * Formats a time interval with the requested granularity.
  *
- * @param $timestamp
+ * @param $interval
  *   The length of the interval in seconds.
  * @param $granularity
  *   How many different units to display in the string.
  * @param $langcode
  *   Optional language code to translate to a language other than
  *   what is used to display the page.
+ *
  * @return
  *   A translated string representation of the interval.
  */
-function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
+function format_interval($interval, $granularity = 2, $langcode = NULL) {
   $units = array(
     '1 year|@count years' => 31536000,
     '1 month|@count months' => 2592000,
@@ -1813,9 +1841,9 @@ function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
   $output = '';
   foreach ($units as $key => $value) {
     $key = explode('|', $key);
-    if ($timestamp >= $value) {
-      $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
-      $timestamp %= $value;
+    if ($interval >= $value) {
+      $output .= ($output ? ' ' : '') . format_plural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
+      $interval %= $value;
       $granularity--;
     }
 
@@ -1928,10 +1956,11 @@ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NUL
 /**
  * Returns an ISO8601 formatted date based on the given date.
  *
- * Can be used as a callback for RDF mappings.
+ * Callback for use within hook_rdf_mapping() implementations.
  *
  * @param $date
  *   A UNIX timestamp.
+ *
  * @return string
  *   An ISO8601 formatted date.
  */
@@ -1942,7 +1971,9 @@ function date_iso8601($date) {
 }
 
 /**
- * Callback function for preg_replace_callback().
+ * Translates a formatted date string.
+ *
+ * Callback for preg_replace_callback() within format_date().
  */
 function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
   // We cache translations to avoid redundant and rather costly calls to t().
@@ -2057,8 +2088,8 @@ function format_username($account) {
  *     Drupal on a web server that cannot be configured to automatically find
  *     index.php, then hook_url_outbound_alter() can be implemented to force
  *     this value to 'index.php'.
- *   - 'entity_type': The entity type of the object that called url(). Only set if
- *     url() is invoked by entity_uri().
+ *   - 'entity_type': The entity type of the object that called url(). Only
+ *     set if url() is invoked by entity_uri().
  *   - 'entity': The entity object (such as a node) for which the URL is being
  *     generated. Only set if url() is invoked by entity_uri().
  *
@@ -2183,7 +2214,7 @@ function url($path = NULL, array $options = array()) {
 }
 
 /**
- * Return TRUE if a path is external to Drupal (e.g. http://example.com).
+ * Returns TRUE if a path is external to Drupal (e.g. http://example.com).
  *
  * If a path cannot be assessed by Drupal's menu handler, then we must
  * treat it as potentially insecure.
@@ -2191,6 +2222,7 @@ function url($path = NULL, array $options = array()) {
  * @param $path
  *   The internal path or external URL being linked to, such as "node/34" or
  *   "http://example.com/foo".
+ *
  * @return
  *   Boolean TRUE or FALSE, where TRUE indicates an external path.
  */
@@ -2203,7 +2235,7 @@ function url_is_external($path) {
 }
 
 /**
- * Format an attribute string for a HTTP header.
+ * Formats an attribute string for an HTTP header.
  *
  * @param $attributes
  *   An associative array of attributes such as 'rel'.
@@ -2225,7 +2257,7 @@ function drupal_http_header_attributes(array $attributes = array()) {
 }
 
 /**
- * Converts an associative array to an attribute string for use in XML/HTML tags.
+ * Converts an associative array to an XML/HTML tag attribute string.
  *
  * Each array key and its value will be formatted into an attribute string.
  * If a value is itself an array, then its elements are concatenated to a single
@@ -2338,7 +2370,7 @@ function l($text, $path, array $options = array()) {
     // rendering.
     if (variable_get('theme_link', TRUE)) {
       drupal_theme_initialize();
-      $registry = theme_get_registry();
+      $registry = theme_get_registry(FALSE);
       // We don't want to duplicate functionality that's in theme(), so any
       // hint of a module or theme doing anything at all special with the 'link'
       // theme hook should simply result in theme() being called. This includes
@@ -2446,7 +2478,7 @@ function drupal_deliver_page($page_callback_result, $default_delivery_callback =
 }
 
 /**
- * Package and send the result of a page callback to the browser as HTML.
+ * Packages and sends the result of a page callback to the browser as HTML.
  *
  * @param $page_callback_result
  *   The result of a page callback. Can be one of:
@@ -2466,6 +2498,10 @@ function drupal_deliver_html_page($page_callback_result) {
     drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
   }
 
+  // Send appropriate HTTP-Header for browsers and search engines.
+  global $language;
+  drupal_add_http_header('Content-Language', $language->language);
+
   // Menu status constants are integers; page content is a string or array.
   if (is_int($page_callback_result)) {
     // @todo: Break these up into separate functions?
@@ -2551,7 +2587,7 @@ function drupal_deliver_html_page($page_callback_result) {
 }
 
 /**
- * Perform end-of-request tasks.
+ * Performs end-of-request tasks.
  *
  * This function sets the page cache if appropriate, and allows modules to
  * react to the closing of the page by calling hook_exit().
@@ -2578,7 +2614,7 @@ function drupal_page_footer() {
 }
 
 /**
- * Perform end-of-request tasks.
+ * Performs end-of-request tasks.
  *
  * In some cases page requests need to end without calling drupal_page_footer().
  * In these cases, call drupal_exit() instead. There should rarely be a reason
@@ -2600,7 +2636,7 @@ function drupal_exit($destination = NULL) {
 }
 
 /**
- * Form an associative array from a linear array.
+ * Forms an associative array from a linear array.
  *
  * This function walks through the provided array and constructs an associative
  * array out of it. The keys of the resulting array will be the values of the
@@ -2676,10 +2712,10 @@ function drupal_get_path($type, $name) {
 }
 
 /**
- * Return the base URL path (i.e., directory) of the Drupal installation.
+ * Returns the base URL path (i.e., directory) of the Drupal installation.
  *
- * base_path() prefixes and suffixes a "/" onto the returned path if the path is
- * not empty. At the very least, this will return "/".
+ * base_path() adds a "/" to the beginning and end of the returned path if the
+ * path is not empty. At the very least, this will return "/".
  *
  * Examples:
  * - http://example.com returns "/" because the path is empty.
@@ -2690,12 +2726,12 @@ function base_path() {
 }
 
 /**
- * Add a LINK tag with a distinct 'rel' attribute to the page's HEAD.
+ * Adds a LINK tag with a distinct 'rel' attribute to the page's HEAD.
  *
- * This function can be called as long the HTML header hasn't been sent,
- * which on normal pages is up through the preprocess step of theme('html').
- * Adding a link will overwrite a prior link with the exact same 'rel' and
- * 'href' attributes.
+ * This function can be called as long the HTML header hasn't been sent, which
+ * on normal pages is up through the preprocess step of theme('html'). Adding
+ * a link will overwrite a prior link with the exact same 'rel' and 'href'
+ * attributes.
  *
  * @param $attributes
  *   Associative array of element attributes including 'href' and 'rel'.
@@ -2759,8 +2795,8 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
  *     See drupal_get_css() where the overrides are performed. Also, if the
  *     direction of the current language is right-to-left (Hebrew, Arabic,
  *     etc.), the function will also look for an RTL CSS file and append it to
- *     the list. The name of this file should have an '-rtl.css' suffix.  For
- *     example a CSS file called 'mymodule-name.css' will have a
+ *     the list. The name of this file should have an '-rtl.css' suffix. For
+ *     example, a CSS file called 'mymodule-name.css' will have a
  *     'mymodule-name-rtl.css' file added to the list, if exists in the same
  *     directory. This CSS file should contain overrides for properties which
  *     should be reversed or otherwise different in a right-to-left display.
@@ -2892,7 +2928,7 @@ function drupal_add_css($data = NULL, $options = NULL) {
 }
 
 /**
- * Returns a themed representation of all stylesheets that should be attached to the page.
+ * Returns a themed representation of all stylesheets to attach to the page.
  *
  * It loads the CSS in order, with 'module' first, then 'theme' afterwards.
  * This ensures proper cascading of styles so themes can easily override
@@ -2938,7 +2974,7 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
   foreach ($css as $key => $item) {
     if ($item['type'] == 'file') {
       // If defined, force a unique basename for this file.
-      $basename = isset($item['basename']) ? $item['basename'] : basename($item['data']);
+      $basename = isset($item['basename']) ? $item['basename'] : drupal_basename($item['data']);
       if (isset($previous_item[$basename])) {
         // Remove the previous item that shared the same base name.
         unset($css[$previous_item[$basename]]);
@@ -2962,11 +2998,24 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
 }
 
 /**
- * Function used by uasort to sort the array structures returned by drupal_add_css() and drupal_add_js().
+ * Sorts CSS and JavaScript resources.
+ *
+ * Callback for uasort() within:
+ * - drupal_get_css()
+ * - drupal_get_js()
  *
  * This sort order helps optimize front-end performance while providing modules
  * and themes with the necessary control for ordering the CSS and JavaScript
  * appearing on a page.
+ *
+ * @param $a
+ *   First item for comparison. The compared items should be associative arrays
+ *   of member items from drupal_add_css() or drupal_add_js().
+ * @param $b
+ *   Second item for comparison.
+ *
+ * @see drupal_add_css()
+ * @see drupal_add_js()
  */
 function drupal_sort_css_js($a, $b) {
   // First order by group, so that, for example, all items in the CSS_SYSTEM
@@ -3033,6 +3082,7 @@ function drupal_sort_css_js($a, $b) {
  *   'items' key, which is the subset of items from $css that are in the group.
  *
  * @see drupal_pre_render_styles()
+ * @see system_element_info()
  */
 function drupal_group_css($css) {
   $groups = array();
@@ -3115,6 +3165,7 @@ function drupal_group_css($css) {
  *
  * @see drupal_group_css()
  * @see drupal_pre_render_styles()
+ * @see system_element_info()
  */
 function drupal_aggregate_css(&$css_groups) {
   $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
@@ -3376,8 +3427,8 @@ function drupal_pre_render_styles($elements) {
  * in $css while the value is the cache file name. The cache file is generated
  * in two cases. First, if there is no file name value for the key, which will
  * happen if a new file name has been added to $css or after the lookup
- * variable is emptied to force a rebuild of the cache.  Second, the cache
- * file is generated if it is missing on disk. Old cache files are not deleted
+ * variable is emptied to force a rebuild of the cache. Second, the cache file
+ * is generated if it is missing on disk. Old cache files are not deleted
  * immediately when the lookup variable is emptied, but are deleted after a set
  * period by drupal_delete_file_if_stale(). This ensures that files referenced
  * by a cached page will still be available.
@@ -3455,9 +3506,7 @@ function drupal_build_css_cache($css) {
 }
 
 /**
- * Helper function for drupal_build_css_cache().
- *
- * This function will prefix all paths within a CSS file.
+ * Prefixes all paths within a CSS file for drupal_build_css_cache().
  */
 function _drupal_build_css_path($matches, $base = NULL) {
   $_base = &drupal_static(__FUNCTION__);
@@ -3528,13 +3577,14 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
 }
 
 /**
- * Process the contents of a stylesheet for aggregation.
+ * Processes the contents of a stylesheet for aggregation.
  *
  * @param $contents
  *   The contents of the stylesheet.
  * @param $optimize
  *   (optional) Boolean whether CSS contents should be minified. Defaults to
  *   FALSE.
+ *
  * @return
  *   Contents of the stylesheet including the imported stylesheets.
  */
@@ -3630,7 +3680,7 @@ function drupal_delete_file_if_stale($uri) {
 }
 
 /**
- * Prepare a string for use as a valid CSS identifier (element, class or ID name).
+ * Prepares a string for use as a CSS identifier (element, class, or ID name).
  *
  * http://www.w3.org/TR/CSS21/syndata.html#characters shows the syntax for valid
  * CSS identifiers (including element names, classes, and IDs in selectors.)
@@ -3639,6 +3689,7 @@ function drupal_delete_file_if_stale($uri) {
  *   The identifier to clean.
  * @param $filter
  *   An array of string replacements to use on the identifier.
+ *
  * @return
  *   The cleaned identifier.
  */
@@ -3660,13 +3711,14 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
 }
 
 /**
- * Prepare a string for use as a valid class name.
+ * Prepares a string for use as a valid class name.
  *
  * Do not pass one string containing multiple classes as they will be
  * incorrectly concatenated with dashes, i.e. "one two" will become "one-two".
  *
  * @param $class
  *   The class name to clean.
+ *
  * @return
  *   The cleaned class name.
  */
@@ -3675,7 +3727,7 @@ function drupal_html_class($class) {
 }
 
 /**
- * Prepare a string for use as a valid HTML ID and guarantee uniqueness.
+ * Prepares a string for use as a valid HTML ID and guarantees uniqueness.
  *
  * This function ensures that each passed HTML ID value only exists once on the
  * page. By tracking the already returned ids, this function enables forms,
@@ -3806,7 +3858,7 @@ function drupal_region_class($region) {
  *   to tell the user that a new message arrived, by opening a pop up, alert
  *   box, etc.). This should only be used for JavaScript that cannot be executed
  *   from a file. When adding inline code, make sure that you are not relying on
- *   $() being the jQuery function.  Wrap your code in
+ *   $() being the jQuery function. Wrap your code in
  *   @code (function ($) {... })(jQuery); @endcode
  *   or use jQuery() instead of $().
  * - Add external JavaScript ('external'): Allows the inclusion of external
@@ -3926,7 +3978,7 @@ function drupal_region_class($region) {
  *       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;
- *     tag.  Defaults to FALSE.
+ *     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
  *     a JavaScript file. Defaults to TRUE.
@@ -4023,6 +4075,7 @@ function drupal_add_js($data = NULL, $options = NULL) {
  *
  * @param $data
  *   (optional) The default data parameter for the JavaScript item array.
+ *
  * @see drupal_get_js()
  * @see drupal_add_js()
  */
@@ -4066,8 +4119,10 @@ function drupal_js_defaults($data = NULL) {
  *   (optional) If set to TRUE, this function skips calling drupal_alter() on
  *   $javascript, useful when the calling function passes a $javascript array
  *   that has already been altered.
+ *
  * @return
  *   All JavaScript code segments and includes for the scope as HTML tags.
+ *
  * @see drupal_add_js()
  * @see locale_js_alter()
  * @see drupal_js_defaults()
@@ -4239,7 +4294,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
  * );
  * @endcode
  *
- * 'js', 'css', and 'library' are types that get special handling.  For any
+ * 'js', 'css', and 'library' are types that get special handling. For any
  * other kind of attached data, the array key must be the full name of the
  * callback function and each value an array of arguments. For example:
  * @code
@@ -4590,16 +4645,16 @@ function drupal_get_library($module, $name = NULL) {
 }
 
 /**
- * Assist in adding the tableDrag JavaScript behavior to a themed table.
+ * Assists in adding the tableDrag JavaScript behavior to a themed table.
  *
  * Draggable tables should be used wherever an outline or list of sortable items
  * needs to be arranged by an end-user. Draggable tables are very flexible and
  * can manipulate the value of form elements placed within individual columns.
  *
- * To set up a table to use drag and drop in place of weight select-lists or
- * in place of a form that contains parent relationships, the form must be
- * themed into a table. The table must have an id attribute set. If using
- * theme_table(), the id may be set as such:
+ * To set up a table to use drag and drop in place of weight select-lists or in
+ * place of a form that contains parent relationships, the form must be themed
+ * into a table. The table must have an ID attribute set. If using
+ * theme_table(), the ID may be set as follows:
  * @code
  * $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'my-module-table')));
  * return $output;
@@ -4614,8 +4669,8 @@ function drupal_get_library($module, $name = NULL) {
  * $form['my_elements'][$delta]['weight']['#attributes']['class'] = array('my-elements-weight');
  * @endcode
  *
- * Each row of the table must also have a class of "draggable" in order to enable the
- * drag handles:
+ * Each row of the table must also have a class of "draggable" in order to
+ * enable the drag handles:
  * @code
  * $row = array(...);
  * $rows[] = array(
@@ -4635,8 +4690,8 @@ function drupal_get_library($module, $name = NULL) {
  * @endcode
  *
  * In a more complex case where there are several groups in one column (such as
- * the block regions on the admin/structure/block page), a separate subgroup class
- * must also be added to differentiate the groups.
+ * the block regions on the admin/structure/block page), a separate subgroup
+ * class must also be added to differentiate the groups.
  * @code
  * $form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = array('my-elements-weight', 'my-elements-weight-' . $region);
  * @endcode
@@ -4653,14 +4708,14 @@ function drupal_get_library($module, $name = NULL) {
  *
  * In a situation where tree relationships are present, adding multiple
  * subgroups is not necessary, because the table will contain indentations that
- * provide enough information about the sibling and parent relationships.
- * See theme_menu_overview_form() for an example creating a table containing
- * parent relationships.
- *
- * Please note that this function should be called from the theme layer, such as
- * in a .tpl.php file, theme_ function, or in a template_preprocess function,
- * not in a form declaration. Though the same JavaScript could be added to the
- * page using drupal_add_js() directly, this function helps keep template files
+ * provide enough information about the sibling and parent relationships. See
+ * theme_menu_overview_form() for an example creating a table containing parent
+ * relationships.
+ *
+ * Note that this function should be called from the theme layer, such as in a
+ * .tpl.php file, theme_ function, or in a template_preprocess function, not in
+ * a form declaration. Though the same JavaScript could be added to the page
+ * using drupal_add_js() directly, this function helps keep template files
  * clean and readable. It also prevents tabledrag.js from being added twice
  * accidentally.
  *
@@ -4733,8 +4788,8 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro
  * $files while the value is the cache file name. The cache file is generated
  * in two cases. First, if there is no file name value for the key, which will
  * happen if a new file name has been added to $files or after the lookup
- * variable is emptied to force a rebuild of the cache.  Second, the cache
- * file is generated if it is missing on disk. Old cache files are not deleted
+ * variable is emptied to force a rebuild of the cache. Second, the cache file
+ * is generated if it is missing on disk. Old cache files are not deleted
  * immediately when the lookup variable is emptied, but are deleted after a set
  * period by drupal_delete_file_if_stale(). This ensures that files referenced
  * by a cached page will still be available.
@@ -4800,14 +4855,29 @@ function drupal_clear_js_cache() {
 /**
  * Converts a PHP variable into its JavaScript equivalent.
  *
- * We use HTML-safe strings, i.e. with <, > and & escaped.
+ * We use HTML-safe strings, with several characters escaped.
  *
  * @see drupal_json_decode()
+ * @see drupal_json_encode_helper()
  * @ingroup php_wrappers
  */
 function drupal_json_encode($var) {
-  // json_encode() does not escape <, > and &, so we do it with str_replace().
-  return str_replace(array('<', '>', '&'), array('\u003c', '\u003e', '\u0026'), json_encode($var));
+  // The PHP version cannot change within a request.
+  static $php530;
+
+  if (!isset($php530)) {
+    $php530 = version_compare(PHP_VERSION, '5.3.0', '>=');
+  }
+
+  if ($php530) {
+    // Encode <, >, ', &, and " using the json_encode() options parameter.
+    return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
+  }
+
+  // json_encode() escapes <, >, ', &, and " using its options parameter, but
+  // does not support this parameter prior to PHP 5.3.0.  Use a helper instead.
+  include_once DRUPAL_ROOT . '/includes/json-encode.inc';
+  return drupal_json_encode_helper($var);
 }
 
 /**
@@ -4821,7 +4891,7 @@ function drupal_json_decode($var) {
 }
 
 /**
- * Return data in JSON format.
+ * Returns data in JSON format.
  *
  * This function should be used for JavaScript callback functions returning
  * data in JSON format. It sets the header for JavaScript output.
@@ -4839,7 +4909,7 @@ function drupal_json_output($var = NULL) {
 }
 
 /**
- * Get a salt useful for hardening against SQL injection.
+ * Gets a salt useful for hardening against SQL injection.
  *
  * @return
  *   A salt based on information in settings.php, not in the database.
@@ -4852,7 +4922,7 @@ function drupal_get_hash_salt() {
 }
 
 /**
- * Ensure the private key variable used to generate tokens is set.
+ * Ensures the private key variable used to generate tokens is set.
  *
  * @return
  *   The private key.
@@ -4866,7 +4936,7 @@ function drupal_get_private_key() {
 }
 
 /**
- * Generate a token based on $value, the current user session and private key.
+ * Generates a token based on $value, the user session, and the private key.
  *
  * @param $value
  *   An additional value to base the token on.
@@ -4876,7 +4946,7 @@ function drupal_get_token($value = '') {
 }
 
 /**
- * Validate a token based on $value, the current user session and private key.
+ * Validates a token based on $value, the user session, and the private key.
  *
  * @param $token
  *   The token to be validated.
@@ -4884,6 +4954,7 @@ function drupal_get_token($value = '') {
  *   An additional value to base the token on.
  * @param $skip_anonymous
  *   Set to true to skip token validation for anonymous users.
+ *
  * @return
  *   True for a valid token, false for an invalid token. When $skip_anonymous
  *   is true, the return value will always be true for anonymous users.
@@ -4952,7 +5023,7 @@ function _drupal_bootstrap_full() {
 }
 
 /**
- * Store the current page in the cache.
+ * Stores the current page in the cache.
  *
  * If page_compression is enabled, a gzipped version of the page is stored in
  * the cache to avoid compressing the output on each request. The cache entry
@@ -5004,10 +5075,10 @@ function drupal_page_set_cache() {
 /**
  * Executes a cron run when called.
  *
- * Do not call this function from test, use $this->cronRun() instead.
+ * Do not call this function from a test. Use $this->cronRun() instead.
  *
  * @return
- *   Returns TRUE if ran successfully
+ *   TRUE if cron ran successfully.
  */
 function drupal_cron_run() {
   // Allow execution to continue even if the request gets canceled.
@@ -5040,7 +5111,7 @@ function drupal_cron_run() {
     foreach ($queues as $queue_name => $info) {
       DrupalQueue::get($queue_name)->createQueue();
     }
-    // Register shutdown callback
+    // Register shutdown callback.
     drupal_register_shutdown_function('drupal_cron_cleanup');
 
     // Iterate through the modules calling their cron handlers (if any):
@@ -5054,7 +5125,7 @@ function drupal_cron_run() {
       }
     }
 
-    // Record cron time
+    // Record cron time.
     variable_set('cron_last', REQUEST_TIME);
     watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
 
@@ -5082,14 +5153,17 @@ function drupal_cron_run() {
 }
 
 /**
- * Shutdown function for cron cleanup.
+ * Shutdown function: Performs cron cleanup.
+ *
+ * @see drupal_cron_run()
+ * @see drupal_register_shutdown_function()
  */
 function drupal_cron_cleanup() {
   // See if the semaphore is still locked.
   if (variable_get('cron_semaphore', FALSE)) {
     watchdog('cron', 'Cron run exceeded the time limit and was aborted.', array(), WATCHDOG_WARNING);
 
-    // Release cron semaphore
+    // Release cron semaphore.
     variable_del('cron_semaphore');
   }
 }
@@ -5158,14 +5232,14 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
     $searchdir[] = "profiles/$profile/$directory";
   }
 
-  // Always search sites/all/* as well as the global directories
+  // Always search sites/all/* as well as the global directories.
   $searchdir[] = 'sites/all/' . $directory;
 
   if (file_exists("$config/$directory")) {
     $searchdir[] = "$config/$directory";
   }
 
-  // Get current list of items
+  // Get current list of items.
   if (!function_exists('file_scan_directory')) {
     require_once DRUPAL_ROOT . '/includes/file.inc';
   }
@@ -5201,7 +5275,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
 }
 
 /**
- * Set the main page content value for later use.
+ * Sets the main page content value for later use.
  *
  * Given the nature of the Drupal page handling, this will be called once with
  * a string or array. We store that and return it later as the block is being
@@ -5209,6 +5283,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
  *
  * @param $content
  *   A string or renderable array representing the body of the page.
+ *
  * @return
  *   If called without $content, a renderable array representing the body of
  *   the page.
@@ -5459,13 +5534,13 @@ function drupal_pre_render_links($element) {
  * Note that if also a #theme is defined for the element, then the result of
  * the theme callback will override #children.
  *
- * @see drupal_render()
- *
  * @param $elements
  *   A structured array using the #markup key.
  *
  * @return
  *   The passed-in elements, but #markup appended to #children.
+ *
+ * @see drupal_render()
  */
 function drupal_pre_render_markup($elements) {
   $elements['#children'] = $elements['#markup'];
@@ -5478,8 +5553,10 @@ function drupal_pre_render_markup($elements) {
  * @param $page
  *   A string or array representing the content of a page. The array consists of
  *   the following keys:
- *   - #type: Value is always 'page'. This pushes the theming through page.tpl.php (required).
- *   - #show_messages: Suppress drupal_get_message() items. Used by Batch API (optional).
+ *   - #type: Value is always 'page'. This pushes the theming through
+ *     page.tpl.php (required).
+ *   - #show_messages: Suppress drupal_get_message() items. Used by Batch
+ *     API (optional).
  *
  * @see hook_page_alter()
  * @see element_info()
@@ -5556,20 +5633,20 @@ function drupal_render_page($page) {
  * drupal_render() can optionally cache the rendered output of elements to
  * improve performance. To use drupal_render() caching, set the element's #cache
  * property to an associative array with one or several of the following keys:
- *    - 'keys': An array of one or more keys that identify the element. If 'keys'
- *       is set, the cache ID is created automatically from these keys. See
- *       drupal_render_cid_create().
- *    - 'granularity' (optional): Define the cache granularity using binary
- *       combinations of the cache granularity constants, e.g. DRUPAL_CACHE_PER_USER
- *       to cache for each user separately or
- *       DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each
- *       page and role. If not specified the element is cached globally for each
- *       theme and language.
- *    - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
- *       If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
- *       have special requirements.
- *    - 'expire': Set to one of the cache lifetime constants.
- *    - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
+ * - 'keys': An array of one or more keys that identify the element. If 'keys'
+ *   is set, the cache ID is created automatically from these keys. See
+ *   drupal_render_cid_create().
+ * - 'granularity' (optional): Define the cache granularity using binary
+ *   combinations of the cache granularity constants, e.g.
+ *   DRUPAL_CACHE_PER_USER to cache for each user separately or
+ *   DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each
+ *   page and role. If not specified the element is cached globally for each
+ *   theme and language.
+ * - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
+ *   If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
+ *   have special requirements.
+ * - 'expire': Set to one of the cache lifetime constants.
+ * - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
  *
  * This function is usually called from within another function, like
  * drupal_get_form() or a theme function. Elements are sorted internally
@@ -5586,6 +5663,7 @@ function drupal_render_page($page) {
  *
  * @param $elements
  *   The structured array describing the data to be rendered.
+ *
  * @return
  *   The rendered HTML.
  */
@@ -5601,8 +5679,11 @@ function drupal_render(&$elements) {
   }
 
   // Try to fetch the element's markup from cache and return.
-  if (isset($elements['#cache']) && $cached_output = drupal_render_cache_get($elements)) {
-    return $cached_output;
+  if (isset($elements['#cache'])) {
+    $cached_output = drupal_render_cache_get($elements);
+    if ($cached_output !== FALSE) {
+      return $cached_output;
+    }
   }
 
   // If #markup is set, ensure #type is set. This allows to specify just #markup
@@ -5699,7 +5780,7 @@ function drupal_render(&$elements) {
 }
 
 /**
- * Render children of an element and concatenate them.
+ * 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.
@@ -5724,7 +5805,7 @@ function drupal_render_children(&$element, $children_keys = NULL) {
 }
 
 /**
- * Render an element.
+ * Renders an element.
  *
  * This function renders an element using drupal_render(). The top level
  * element is shown with show() before rendering, so it will always be rendered
@@ -5753,7 +5834,7 @@ function render(&$element) {
 }
 
 /**
- * Hide an element from later rendering.
+ * Hides an element from later rendering.
  *
  * The first time render() or drupal_render() is called on an element tree,
  * as each element in the tree is rendered, it is marked with a #printed flag
@@ -5779,7 +5860,7 @@ function hide(&$element) {
 }
 
 /**
- * Show a hidden element for later rendering.
+ * Shows a hidden element for later rendering.
  *
  * You can also use render($element), which shows the element while rendering
  * it.
@@ -5808,16 +5889,17 @@ function show(&$element) {
 }
 
 /**
- * Get the rendered output of a renderable element from cache.
- *
- * @see drupal_render()
- * @see drupal_render_cache_set()
+ * Gets the rendered output of a renderable element from the cache.
  *
  * @param $elements
  *   A renderable array.
+ *
  * @return
  *   A markup string containing the rendered content of the element, or FALSE
  *   if no cached copy of the element is available.
+ *
+ * @see drupal_render()
+ * @see drupal_render_cache_set()
  */
 function drupal_render_cache_get($elements) {
   if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')) || !$cid = drupal_render_cid_create($elements)) {
@@ -5838,17 +5920,17 @@ function drupal_render_cache_get($elements) {
 }
 
 /**
- * Cache the rendered output of a renderable element.
- *
- * This is called by drupal_render() if the #cache property is set on an element.
+ * Caches the rendered output of a renderable element.
  *
- * @see drupal_render()
- * @see drupal_render_cache_get()
+ * This is called by drupal_render() if the #cache property is set on an
+ * element.
  *
  * @param $markup
  *   The rendered output string of $elements.
  * @param $elements
  *   A renderable array.
+ *
+ * @see drupal_render_cache_get()
  */
 function drupal_render_cache_set(&$markup, $elements) {
   // Create the cache ID for the element.
@@ -5874,7 +5956,7 @@ function drupal_render_cache_set(&$markup, $elements) {
 }
 
 /**
- * Collect #attached for an element and all child elements into a single array.
+ * Collects #attached for an element and its children into a single array.
  *
  * When caching elements, it is necessary to collect all libraries, JavaScript
  * and CSS into a single array, from both the element itself and all child
@@ -5917,9 +5999,10 @@ function drupal_render_collect_attached($elements, $return = FALSE) {
 }
 
 /**
- * Prepare an element for caching based on a query. This smart caching strategy
- * saves Drupal from querying and rendering to HTML when the underlying query is
- * unchanged.
+ * Prepares an element for caching based on a query.
+ *
+ * This smart caching strategy saves Drupal from querying and rendering to HTML
+ * when the underlying query is unchanged.
  *
  * Expensive queries should use the query builder to create the query and then
  * call this function. Executing the query and formatting results should happen
@@ -5957,12 +6040,15 @@ function drupal_render_cache_by_query($query, $function, $expire = CACHE_TEMPORA
 }
 
 /**
- * Helper function for building cache ids.
+ * Returns cache ID parts for building a cache ID.
  *
  * @param $granularity
- *   One or more cache granularity constants, e.g. DRUPAL_CACHE_PER_USER to cache
- *   for each user separately or DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to
- *   cache separately for each page and role.
+ *   One or more cache granularity constants. For example, to cache separately
+ *   for each user, use DRUPAL_CACHE_PER_USER. To cache separately for each
+ *   page and role, use the expression:
+ *   @code
+ *   DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE
+ *   @endcode
  *
  * @return
  *   An array of cache ID parts, always containing the active theme. If the
@@ -6001,7 +6087,7 @@ function drupal_render_cid_parts($granularity = NULL) {
 }
 
 /**
- * Create the cache ID for a renderable element.
+ * Creates the cache ID for a renderable element.
  *
  * This creates the cache ID string, either by returning the #cache['cid']
  * property if present or by building the cache ID out of the #cache['keys']
@@ -6048,7 +6134,7 @@ function element_sort_by_title($a, $b) {
 }
 
 /**
- * Retrieve the default properties for the defined element type.
+ * Retrieves the default properties for the defined element type.
  *
  * @param $type
  *   An element type as defined by hook_element_info().
@@ -6074,7 +6160,7 @@ function element_info($type) {
 }
 
 /**
- * Retrieve a single property for the defined element type.
+ * Retrieves a single property for the defined element type.
  *
  * @param $type
  *   An element type as defined by hook_element_info().
@@ -6114,21 +6200,21 @@ function drupal_sort_title($a, $b) {
 }
 
 /**
- * Check if the key is a property.
+ * Checks if the key is a property.
  */
 function element_property($key) {
   return $key[0] == '#';
 }
 
 /**
- * Get properties of a structured array element. Properties begin with '#'.
+ * Gets properties of a structured array element (keys beginning with '#').
  */
 function element_properties($element) {
   return array_filter(array_keys((array) $element), 'element_property');
 }
 
 /**
- * Check if the key is a child.
+ * Checks if the key is a child.
  */
 function element_child($key) {
   return !isset($key[0]) || $key[0] != '#';
@@ -6144,6 +6230,7 @@ function element_child($key) {
  *   The element array whose children are to be identified.
  * @param $sort
  *   Boolean to indicate whether the children should be sorted by weight.
+ *
  * @return
  *   The array keys of the element's children.
  */
@@ -6183,6 +6270,7 @@ function element_children(&$elements, $sort = FALSE) {
  *
  * @param $elements
  *   The parent element.
+ *
  * @return
  *   The array keys of the element's visible children.
  */
@@ -6377,7 +6465,7 @@ function drupal_array_get_nested_value(array &$array, array $parents, &$key_exis
 }
 
 /**
- * Determines whether a nested array with variable depth contains all of the requested keys.
+ * Determines whether a nested array contains the requested keys.
  *
  * This helper function should be used when the depth of the array element to be
  * checked may vary (that is, the number of parent keys is variable). See
@@ -6413,11 +6501,11 @@ function drupal_array_nested_key_exists(array $array, array $parents) {
 }
 
 /**
- * Provide theme registration for themes across .inc files.
+ * Provides theme registration for themes across .inc files.
  */
 function drupal_common_theme() {
   return array(
-    // theme.inc
+    // From theme.inc.
     'html' => array(
       'render element' => 'page',
       'template' => 'html',
@@ -6493,7 +6581,7 @@ function drupal_common_theme() {
     'html_tag' => array(
       'render element' => 'element',
     ),
-    // from theme.maintenance.inc
+    // From theme.maintenance.inc.
     'maintenance_page' => array(
       'variables' => array('content' => NULL, 'show_messages' => TRUE),
       'template' => 'maintenance-page',
@@ -6513,7 +6601,7 @@ function drupal_common_theme() {
     'authorize_report' => array(
       'variables' => array('messages' => array()),
     ),
-    // from pager.inc
+    // From pager.inc.
     'pager' => array(
       'variables' => array('tags' => array(), 'element' => 0, 'parameters' => array(), 'quantity' => 9),
     ),
@@ -6532,7 +6620,7 @@ function drupal_common_theme() {
     'pager_link' => array(
       'variables' => array('text' => NULL, 'page_new' => NULL, 'element' => NULL, 'parameters' => array(), 'attributes' => array()),
     ),
-    // from menu.inc
+    // From menu.inc.
     'menu_link' => array(
       'render element' => 'element',
     ),
@@ -6548,7 +6636,7 @@ function drupal_common_theme() {
     'menu_local_tasks' => array(
       'variables' => array('primary' => array(), 'secondary' => array()),
     ),
-    // from form.inc
+    // From form.inc.
     'select' => array(
       'render element' => 'element',
     ),
@@ -6624,7 +6712,7 @@ function drupal_common_theme() {
  */
 
 /**
- * Creates all tables in a module's hook_schema() implementation.
+ * Creates all tables defined in a module's hook_schema().
  *
  * Note: This function does not pass the module's schema through
  * hook_schema_alter(). The module's tables will be created exactly as the
@@ -6643,7 +6731,7 @@ function drupal_install_schema($module) {
 }
 
 /**
- * Remove all tables that a module defines in its hook_schema().
+ * Removes all tables defined in a module's hook_schema().
  *
  * Note: This function does not pass the module's schema through
  * hook_schema_alter(). The module's tables will be created exactly as the
@@ -6651,6 +6739,7 @@ function drupal_install_schema($module) {
  *
  * @param $module
  *   The module for which the tables will be removed.
+ *
  * @return
  *   An array of arrays with the following key/value pairs:
  *    - success: a boolean indicating whether the query succeeded.
@@ -6706,7 +6795,7 @@ function drupal_get_schema_unprocessed($module, $table = NULL) {
 }
 
 /**
- * Fill in required default values for table definitions returned by hook_schema().
+ * Fills in required default values for table definitions from hook_schema().
  *
  * @param $schema
  *   The schema definition array as it was returned by the module's
@@ -6737,7 +6826,9 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
 }
 
 /**
- * Retrieve a list of fields from a table schema. The list is suitable for use in a SQL query.
+ * Retrieves a list of fields from a table schema.
+ *
+ * The returned list is suitable for use in an SQL query.
  *
  * @param $table
  *   The name of the table from which to retrieve fields.
@@ -6745,7 +6836,7 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
  *   An optional prefix to to all fields.
  *
  * @return An array of fields.
- **/
+ */
 function drupal_schema_fields_sql($table, $prefix = NULL) {
   $schema = drupal_get_schema($table);
   $fields = array_keys($schema['fields']);
@@ -6973,7 +7064,7 @@ function drupal_parse_info_file($filename) {
 }
 
 /**
- * Parse data in Drupal's .info format.
+ * Parses data in Drupal's .info format.
  *
  * Data should be in an .ini-like format to specify values. White-space
  * generally doesn't matter, except inside values:
@@ -7003,6 +7094,7 @@ function drupal_parse_info_file($filename) {
  *
  * @param $data
  *   A string to parse.
+ *
  * @return
  *   The info array.
  *
@@ -7026,19 +7118,19 @@ function drupal_parse_info_format($data) {
     )\s*$                           # Stop at the next end of a line, ignoring trailing whitespace
     @msx', $data, $matches, PREG_SET_ORDER)) {
     foreach ($matches as $match) {
-      // Fetch the key and value string
+      // Fetch the key and value string.
       $i = 0;
       foreach (array('key', 'value1', 'value2', 'value3') as $var) {
         $$var = isset($match[++$i]) ? $match[$i] : '';
       }
       $value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
 
-      // Parse array syntax
+      // Parse array syntax.
       $keys = preg_split('/\]?\[/', rtrim($key, ']'));
       $last = array_pop($keys);
       $parent = &$info;
 
-      // Create nested arrays
+      // Create nested arrays.
       foreach ($keys as $key) {
         if ($key == '') {
           $key = count($parent);
@@ -7054,7 +7146,7 @@ function drupal_parse_info_format($data) {
         $value = $constants[$value];
       }
 
-      // Insert actual value
+      // Insert actual value.
       if ($last == '') {
         $last = count($parent);
       }
@@ -7066,11 +7158,12 @@ function drupal_parse_info_format($data) {
 }
 
 /**
- * Severity levels, as defined in RFC 3164: http://www.ietf.org/rfc/rfc3164.txt.
+ * Returns a list of severity levels, as defined in RFC 3164.
  *
  * @return
  *   Array of the possible severity levels for log messages.
  *
+ * @see http://www.ietf.org/rfc/rfc3164.txt
  * @see watchdog()
  * @ingroup logging_severity_levels
  */
@@ -7089,7 +7182,7 @@ function watchdog_severity_levels() {
 
 
 /**
- * Explode a string of given tags into an array.
+ * Explodes a string of tags into an array.
  *
  * @see drupal_implode_tags()
  */
@@ -7115,7 +7208,7 @@ function drupal_explode_tags($tags) {
 }
 
 /**
- * Implode an array of tags into a string.
+ * Implodes an array of tags into a string.
  *
  * @see drupal_explode_tags()
  */
@@ -7133,7 +7226,7 @@ function drupal_implode_tags($tags) {
 }
 
 /**
- * Flush all cached data on the site.
+ * Flushes all cached data on the site.
  *
  * Empties cache tables, rebuilds the menu cache and theme registries, and
  * invokes a hook so that other modules' cache data can be cleared as well.
@@ -7151,6 +7244,7 @@ function drupal_flush_all_caches() {
   system_rebuild_theme_data();
   drupal_theme_rebuild();
 
+  entity_info_cache_clear();
   node_types_rebuild();
   // node_menu() defines menu items based on node types so it needs to come
   // after node types are rebuilt.
@@ -7174,10 +7268,10 @@ function drupal_flush_all_caches() {
 }
 
 /**
- * Helper function to change query-strings on css/js files.
+ * Changes the dummy query string added to all CSS and JavaScript files.
  *
- * Changes the character added to all css/js files as dummy query-string, so
- * that all browsers are forced to reload fresh files.
+ * Changing the dummy query string appended to CSS and JavaScript files forces
+ * all browsers to reload fresh files.
  */
 function _drupal_flush_css_js() {
   // The timestamp is converted to base 36 in order to make it more compact.
@@ -7185,7 +7279,7 @@ function _drupal_flush_css_js() {
 }
 
 /**
- * Debug function used for outputting debug information.
+ * Outputs debug information.
  *
  * The debug information is passed on to trigger_error() after being converted
  * to a string using _drupal_debug_message().
@@ -7210,10 +7304,11 @@ function debug($data, $label = NULL, $print_r = FALSE) {
 }
 
 /**
- * Parse a dependency for comparison by drupal_check_incompatibility().
+ * Parses a dependency for comparison by drupal_check_incompatibility().
  *
  * @param $dependency
  *   A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'.
+ *
  * @return
  *   An associative array with three keys:
  *   - 'name' includes the name of the thing to depend on (e.g. 'foo').
@@ -7267,12 +7362,13 @@ function drupal_parse_dependency($dependency) {
 }
 
 /**
- * Check whether a version is compatible with a given dependency.
+ * Checks whether a version is compatible with a given dependency.
  *
  * @param $v
  *   The parsed dependency structure from drupal_parse_dependency().
  * @param $current_version
  *   The version to check against (like 4.2).
+ *
  * @return
  *   NULL if compatible, otherwise the original dependency version string that
  *   caused the incompatibility.
@@ -7777,11 +7873,12 @@ function archiver_get_extensions() {
 }
 
 /**
- * Create the appropriate archiver for the specified file.
+ * Creates the appropriate archiver for the specified file.
  *
  * @param $file
- *   The full path of the archive file.  Note that stream wrapper
- *   paths are supported, but not remote ones.
+ *   The full path of the archive file. Note that stream wrapper paths are
+ *   supported, but not remote ones.
+ *
  * @return
  *   A newly created instance of the archiver class appropriate
  *   for the specified file, already bound to that file.
@@ -7810,14 +7907,14 @@ function archiver_get_archiver($file) {
 }
 
 /**
- * Drupal Updater registry.
+ * Assembles the Drupal Updater registry.
  *
  * An Updater is a class that knows how to update various parts of the Drupal
  * file system, for example to update modules that have newer releases, or to
  * install a new theme.
  *
  * @return
- *   Returns the Drupal Updater class registry.
+ *   The Drupal Updater class registry.
  *
  * @see hook_updater_info()
  * @see hook_updater_info_alter()
@@ -7833,10 +7930,10 @@ function drupal_get_updaters() {
 }
 
 /**
- * Drupal FileTransfer registry.
+ * Assembles the Drupal FileTransfer registry.
  *
  * @return
- *   Returns the Drupal FileTransfer class registry.
+ *   The Drupal FileTransfer class registry.
  *
  * @see FileTransfer
  * @see hook_filetransfer_info()
index 33000aa..6e40b27 100644 (file)
@@ -1016,9 +1016,9 @@ abstract class DatabaseConnection extends PDO {
       throw new DatabaseTransactionNoActiveException();
     }
     // A previous rollback to an earlier savepoint may mean that the savepoint
-    // in question has already been rolled back.
-    if (!in_array($savepoint_name, $this->transactionLayers)) {
-      return;
+    // in question has already been accidentally committed.
+    if (!isset($this->transactionLayers[$savepoint_name])) {
+      throw new DatabaseTransactionNoActiveException();
     }
 
     // We need to find the point we're rolling back to, all other savepoints
@@ -1096,8 +1096,12 @@ abstract class DatabaseConnection extends PDO {
     if (!$this->supportsTransactions()) {
       return;
     }
+    // The transaction has already been committed earlier. There is nothing we
+    // need to do. If this transaction was part of an earlier out-of-order
+    // rollback, an exception would already have been thrown by
+    // Database::rollback().
     if (!isset($this->transactionLayers[$name])) {
-      throw new DatabaseTransactionNoActiveException();
+      return;
     }
 
     // Mark this layer as committable.
index 7d5d859..e024a7f 100644 (file)
@@ -37,14 +37,20 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       $dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . (empty($connection_options['port']) ? 3306 : $connection_options['port']);
     }
     $dsn .= ';dbname=' . $connection_options['database'];
-    parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array(
+    // Allow PDO options to be overridden.
+    $connection_options += array(
+      'pdo' => array(),
+    );
+    $connection_options['pdo'] += array(
       // So we don't have to mess around with cursors and unbuffered queries by default.
       PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
       // Because MySQL's prepared statements skip the query cache, because it's dumb.
       PDO::ATTR_EMULATE_PREPARES => TRUE,
       // Force column names to lower case.
       PDO::ATTR_CASE => PDO::CASE_LOWER,
-    ));
+    );
+
+    parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
 
     // Force MySQL to use the UTF-8 character set. Also set the collation, if a
     // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
@@ -56,14 +62,22 @@ class DatabaseConnection_mysql extends DatabaseConnection {
       $this->exec('SET NAMES utf8');
     }
 
-    // Force MySQL's behavior to conform more closely to SQL standards.
-    // This allows Drupal to run almost seamlessly on many different
-    // kinds of database systems. These settings force MySQL to behave
-    // the same as postgresql, or sqlite in regards to syntax interpretation
-    // and invalid data handling. See http://drupal.org/node/344575 for
-    // further discussion. Also, as MySQL 5.5 changed the meaning of
-    // TRADITIONAL we need to spell out the modes one by one.
-    $this->exec("SET sql_mode='ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'");
+    // Set MySQL init_commands if not already defined.  Default Drupal's MySQL
+    // behavior to conform more closely to SQL standards.  This allows Drupal
+    // to run almost seamlessly on many different kinds of database systems.
+    // These settings force MySQL to behave the same as postgresql, or sqlite
+    // in regards to syntax interpretation and invalid data handling.  See
+    // http://drupal.org/node/344575 for further discussion. Also, as MySQL 5.5
+    // changed the meaning of TRADITIONAL we need to spell out the modes one by
+    // one.
+    $connection_options += array(
+      'init_commands' => array(),
+    );
+    $connection_options['init_commands'] += array(
+      'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
+    );
+    // Set connection options.
+    $this->exec(implode('; ', $connection_options['init_commands']));
   }
 
   public function queryRange($query, $from, $count, array $args = array(), array $options = array()) {
@@ -169,8 +183,11 @@ class DatabaseConnection_mysql extends DatabaseConnection {
           // succeed for MySQL error code 1305 ("SAVEPOINT does not exist").
           if ($e->errorInfo[1] == '1305') {
             // If one SAVEPOINT was released automatically, then all were.
-            // Therefore, we keep just the topmost transaction.
-            $this->transactionLayers = array('drupal_transaction' => 'drupal_transaction');
+            // Therefore, clean the transaction stack.
+            $this->transactionLayers = array();
+            // We also have to explain to PDO that the transaction stack has
+            // been cleaned-up.
+            PDO::commit();
           }
           else {
             throw $e;
index 39b4e9b..d42a1cc 100644 (file)
@@ -47,7 +47,12 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
     $this->connectionOptions = $connection_options;
 
     $dsn = 'pgsql:host=' . $connection_options['host'] . ' dbname=' . $connection_options['database'] . ' port=' . $connection_options['port'];
-    parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array(
+
+    // Allow PDO options to be overridden.
+    $connection_options += array(
+      'pdo' => array(),
+    );
+    $connection_options['pdo'] += array(
       // Prepared statements are most effective for performance when queries
       // are recycled (used several times). However, if they are not re-used,
       // prepared statements become ineffecient. Since most of Drupal's
@@ -59,10 +64,16 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
       PDO::ATTR_STRINGIFY_FETCHES => TRUE,
       // Force column names to lower case.
       PDO::ATTR_CASE => PDO::CASE_LOWER,
-    ));
+    );
+    parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
 
     // Force PostgreSQL to use the UTF-8 character set by default.
     $this->exec("SET NAMES 'UTF8'");
+
+    // Execute PostgreSQL init_commands.
+    if (isset($connection_options['init_commands'])) {
+      $this->exec(implode('; ', $connection_options['init_commands']));
+    }
   }
 
   public function query($query, array $args = array(), $options = array()) {
index c779687..6020b0e 100644 (file)
@@ -22,6 +22,9 @@ interface QueryConditionInterface {
    * parameters, they are taken as $field and $value with $operator having a
    * value of IN if $value is an array and = otherwise.
    *
+   * Do not use this method to test for NULL values. Instead, use
+   * QueryConditionInterface::isNull() or QueryConditionInterface::isNotNull().
+   *
    * @param $field
    *   The name of the field to check. If you would like to add a more complex
    *   condition involving operators or functions, use where().
@@ -36,6 +39,9 @@ interface QueryConditionInterface {
    *
    * @return QueryConditionInterface
    *   The called object.
+   *
+   * @see QueryConditionInterface::isNull()
+   * @see QueryConditionInterface::isNotNull()
    */
   public function condition($field, $value = NULL, $operator = NULL);
 
index 9b587ae..7e2af85 100644 (file)
@@ -590,11 +590,13 @@ class SelectQueryExtender implements SelectQueryInterface {
   }
 
   public function hasAllTags() {
-    return call_user_func_array(array($this->query, 'hasAllTags'), func_get_args());
+    $args = func_get_args();
+    return call_user_func_array(array($this->query, 'hasAllTags'), $args);
   }
 
   public function hasAnyTag() {
-    return call_user_func_array(array($this->query, 'hasAnyTags'), func_get_args());
+    $args = func_get_args();
+    return call_user_func_array(array($this->query, 'hasAnyTags'), $args);
   }
 
   public function addMetaData($key, $object) {
@@ -637,16 +639,16 @@ class SelectQueryExtender implements SelectQueryInterface {
   /* Implementations of QueryConditionInterface for the HAVING clause. */
 
   public function havingCondition($field, $value = NULL, $operator = '=') {
-    $this->query->condition($field, $value, $operator, $num_args);
+    $this->query->havingCondition($field, $value, $operator);
     return $this;
   }
 
   public function &havingConditions() {
-    return $this->having->conditions();
+    return $this->query->havingConditions();
   }
 
   public function havingArguments() {
-    return $this->having->arguments();
+    return $this->query->havingArguments();
   }
 
   public function having($snippet, $args = array()) {
@@ -790,31 +792,7 @@ class SelectQueryExtender implements SelectQueryInterface {
   }
 
   public function countQuery() {
-    // Create our new query object that we will mutate into a count query.
-    $count = clone($this);
-
-    // Zero-out existing fields and expressions.
-    $fields =& $count->getFields();
-    $fields = array();
-    $expressions =& $count->getExpressions();
-    $expressions = array();
-
-    // Also remove 'all_fields' statements, which are expanded into tablename.*
-    // when the query is executed.
-    $tables = &$count->getTables();
-    foreach ($tables as $alias => &$table) {
-      unset($table['all_fields']);
-    }
-
-    // Ordering a count query is a waste of cycles, and breaks on some
-    // databases anyway.
-    $orders = &$count->getOrderBy();
-    $orders = array();
-
-    // COUNT() is an expression, so we add that back in.
-    $count->addExpression('COUNT(*)');
-
-    return $count;
+    return $this->query->countQuery();
   }
 
   function isNull($field) {
@@ -836,7 +814,7 @@ class SelectQueryExtender implements SelectQueryInterface {
     $this->query->notExists($select);
     return $this;
   }
-  
+
   public function __toString() {
     return (string) $this->query;
   }
@@ -1005,11 +983,13 @@ class SelectQuery extends Query implements SelectQueryInterface {
   }
 
   public function hasAllTags() {
-    return !(boolean)array_diff(func_get_args(), array_keys($this->alterTags));
+    $args = func_get_args();
+    return !(boolean)array_diff($args, array_keys($this->alterTags));
   }
 
   public function hasAnyTag() {
-    return (boolean)array_intersect(func_get_args(), array_keys($this->alterTags));
+    $args = func_get_args();
+    return (boolean)array_intersect($args, array_keys($this->alterTags));
   }
 
   public function addMetaData($key, $object) {
@@ -1088,7 +1068,7 @@ class SelectQuery extends Query implements SelectQueryInterface {
     $this->where->notExists($select);
     return $this;
   }
-  
+
   public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
     $this->where->compile($connection, $queryPlaceholder);
     $this->having->compile($connection, $queryPlaceholder);
@@ -1172,17 +1152,17 @@ class SelectQuery extends Query implements SelectQueryInterface {
     $this->having->isNotNull($field);
     return $this;
   }
-  
+
   public function havingExists(SelectQueryInterface $select) {
     $this->having->exists($select);
     return $this;
   }
-  
+
   public function havingNotExists(SelectQueryInterface $select) {
     $this->having->notExists($select);
     return $this;
   }
-  
+
   public function forUpdate($set = TRUE) {
     if (isset($set)) {
       $this->forUpdate = $set;
@@ -1451,17 +1431,20 @@ class SelectQuery extends Query implements SelectQueryInterface {
     $count = clone($this);
 
     $group_by = $count->getGroupBy();
+    $having = $count->havingConditions();
 
-    if (!$count->distinct) {
+    if (!$count->distinct && !isset($having[0])) {
       // When not executing a distinct query, we can zero-out existing fields
-      // and expressions that are not used by a GROUP BY.  Fields listed in
-      // the GROUP BY clause need to be present in the query.
+      // and expressions that are not used by a GROUP BY or HAVING. Fields
+      // listed in a GROUP BY or HAVING clause need to be present in the
+      // query.
       $fields =& $count->getFields();
       foreach (array_keys($fields) as $field) {
         if (empty($group_by[$field])) {
           unset($fields[$field]);
         }
       }
+
       $expressions =& $count->getExpressions();
       foreach (array_keys($expressions) as $field) {
         if (empty($group_by[$field])) {
index 3e2490b..b4e41b5 100644 (file)
@@ -59,16 +59,21 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
     $this->statementClass = NULL;
 
     // This driver defaults to transaction support, except if explicitly passed FALSE.
-    $this->transactionSupport = !isset($connection_options['transactions']) || $connection_options['transactions'] !== FALSE;
+    $this->transactionSupport = $this->transactionalDDLSupport = !isset($connection_options['transactions']) || $connection_options['transactions'] !== FALSE;
 
     $this->connectionOptions = $connection_options;
 
-    parent::__construct('sqlite:' . $connection_options['database'], '', '', array(
+    // Allow PDO options to be overridden.
+    $connection_options += array(
+      'pdo' => array(),
+    );
+    $connection_options['pdo'] += array(
       // Force column names to lower case.
       PDO::ATTR_CASE => PDO::CASE_LOWER,
       // Convert numeric values to strings when fetching.
       PDO::ATTR_STRINGIFY_FETCHES => TRUE,
-    ));
+    );
+    parent::__construct('sqlite:' . $connection_options['database'], '', '', $connection_options['pdo']);
 
     // Attach one database for each registered prefix.
     $prefixes = $this->prefixes;
@@ -103,6 +108,11 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
     $this->sqliteCreateFunction('substring', array($this, 'sqlFunctionSubstring'), 3);
     $this->sqliteCreateFunction('substring_index', array($this, 'sqlFunctionSubstringIndex'), 3);
     $this->sqliteCreateFunction('rand', array($this, 'sqlFunctionRand'));
+
+    // Execute sqlite init_commands.
+    if (isset($connection_options['init_commands'])) {
+      $this->exec(implode('; ', $connection_options['init_commands']));
+    }
   }
 
   /**
index 40e8349..7fd6c71 100644 (file)
@@ -770,7 +770,7 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
     $file = clone $source;
     $file->fid = NULL;
     $file->uri = $uri;
-    $file->filename = basename($uri);
+    $file->filename = drupal_basename($uri);
     // If we are replacing an existing file re-use its database record.
     if ($replace == FILE_EXISTS_REPLACE) {
       $existing_files = file_load_multiple(array(), array('uri' => $uri));
@@ -783,7 +783,7 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
     // If we are renaming around an existing file (rather than a directory),
     // use its basename for the filename.
     elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
-      $file->filename = basename($destination);
+      $file->filename = drupal_basename($destination);
     }
 
     $file = file_save($file);
@@ -828,6 +828,10 @@ function file_valid_uri($uri) {
  *   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.
+ * - 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
+ *   https://bugs.php.net/bug.php?id=60456
  *
  * @param $source
  *   A string specifying the filepath or URI of the source file.
@@ -867,14 +871,14 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
 
   // Build a destination URI if necessary.
   if (!isset($destination)) {
-    $destination = file_build_uri(basename($source));
+    $destination = file_build_uri(drupal_basename($source));
   }
 
 
   // Prepare the destination directory.
   if (file_prepare_directory($destination)) {
     // The destination is already a directory, so append the source basename.
-    $destination = file_stream_wrapper_uri_normalize($destination . '/' . basename($source));
+    $destination = file_stream_wrapper_uri_normalize($destination . '/' . drupal_basename($source));
   }
   else {
     // Perhaps $destination is a dir/file?
@@ -907,8 +911,12 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
   file_ensure_htaccess();
   // Perform the copy operation.
   if (!@copy($source, $destination)) {
-    watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination), WATCHDOG_ERROR);
-    return FALSE;
+    // If the copy failed and realpaths exist, retry the operation using them
+    // instead.
+    if ($real_source === FALSE || $real_destination === FALSE || !@copy($real_source, $real_destination)) {
+      watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination), WATCHDOG_ERROR);
+      return FALSE;
+    }
   }
 
   // Set the permissions on the new file.
@@ -950,7 +958,7 @@ function file_destination($destination, $replace) {
         break;
 
       case FILE_EXISTS_RENAME:
-        $basename = basename($destination);
+        $basename = drupal_basename($destination);
         $directory = drupal_dirname($destination);
         $destination = file_create_filename($basename, $directory);
         break;
@@ -1025,7 +1033,7 @@ function file_move(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
     // If we are renaming around an existing file (rather than a directory),
     // use its basename for the filename.
     elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
-      $file->filename = basename($destination);
+      $file->filename = drupal_basename($destination);
     }
 
     $file = file_save($file);
@@ -1138,7 +1146,7 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
 }
 
 /**
- * Undo the effect of upload_munge_filename().
+ * Undo the effect of file_munge_filename().
  *
  * @param $filename
  *   String with the filename to be unmunged.
@@ -1443,7 +1451,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
   $file = new stdClass();
   $file->uid      = $user->uid;
   $file->status   = 0;
-  $file->filename = trim(basename($_FILES['files']['name'][$source]), '.');
+  $file->filename = trim(drupal_basename($_FILES['files']['name'][$source]), '.');
   $file->uri      = $_FILES['files']['tmp_name'][$source];
   $file->filemime = file_get_mimetype($file->filename);
   $file->filesize = $_FILES['files']['size'][$source];
@@ -1841,7 +1849,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
     $file = new stdClass();
     $file->fid = NULL;
     $file->uri = $uri;
-    $file->filename = basename($uri);
+    $file->filename = drupal_basename($uri);
     $file->filemime = file_get_mimetype($file->uri);
     $file->uid      = $user->uid;
     $file->status   = FILE_STATUS_PERMANENT;
@@ -1857,7 +1865,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
     // If we are renaming around an existing file (rather than a directory),
     // use its basename for the filename.
     elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
-      $file->filename = basename($destination);
+      $file->filename = drupal_basename($destination);
     }
 
     return file_save($file);
@@ -2267,6 +2275,35 @@ function drupal_dirname($uri) {
 }
 
 /**
+ * Gets the filename from a given path.
+ *
+ * PHP's basename() does not properly support streams or filenames beginning
+ * with a non-US-ASCII character.
+ *
+ * @see http://bugs.php.net/bug.php?id=37738
+ * @see basename()
+ *
+ * @ingroup php_wrappers
+ */
+function drupal_basename($uri, $suffix = NULL) {
+  $separators = '/';
+  if (DIRECTORY_SEPARATOR != '/') {
+    // For Windows OS add special separator.
+    $separators .= DIRECTORY_SEPARATOR;
+  }
+  // Remove right-most slashes when $uri points to directory.
+  $uri = rtrim($uri, $separators);
+  // Returns the trailing part of the $uri starting after one of the directory
+  // separators.
+  $filename = preg_match('@[^' . preg_quote($separators, '@') . ']+$@', $uri, $matches) ? $matches[0] : '';
+  // Cuts off a suffix from the filename.
+  if ($suffix) {
+    $filename = preg_replace('@' . preg_quote($suffix, '@') . '$@', '', $filename);
+  }
+  return $filename;
+}
+
+/**
  * Creates a directory using Drupal's default mode.
  *
  * PHP's mkdir() does not respect Drupal's default permissions mode. If a mode
@@ -2362,7 +2399,7 @@ function drupal_tempnam($directory, $prefix) {
     $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
 
     if ($filename = tempnam($wrapper->getDirectoryPath(), $prefix)) {
-      return $scheme . '://' . basename($filename);
+      return $scheme . '://' . drupal_basename($filename);
     }
     else {
       return FALSE;
index 2083da9..bd2057c 100644 (file)
@@ -211,7 +211,7 @@ abstract class FileTransfer {
    */
   protected function copyDirectoryJailed($source, $destination) {
     if ($this->isDirectory($destination)) {
-      $destination = $destination . '/' . basename($source);
+      $destination = $destination . '/' . drupal_basename($source);
     }
     $this->createDirectory($destination);
     foreach (new RecursiveIteratorIterator(new SkipDotsRecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
@@ -302,9 +302,9 @@ abstract class FileTransfer {
     $chroot = '';
     while (count($parts)) {
       $check = implode($parts, '/');
-      if ($this->isFile($check . '/' . basename(__FILE__))) {
+      if ($this->isFile($check . '/' . drupal_basename(__FILE__))) {
         // Remove the trailing slash.
-        return substr($chroot,0,-1);
+        return substr($chroot, 0, -1);
       }
       $chroot .= array_shift($parts) . '/';
     }
index e0bc9cb..3d5f6f2 100644 (file)
@@ -16,7 +16,7 @@
  * \@see user_pass_validate().
  * \@see user_pass_submit().
  *
- * @} End of "defgroup forms".
+ * @}
  */
 
 /**
@@ -156,6 +156,8 @@ function drupal_get_form($form_id) {
  *       automatically loaded by form_get_cache(). By default the current menu
  *       router item's 'file' definition is added, if any. Use
  *       form_load_include() to add include files from a form constructor.
+ *     - base_form_id: Identification for a base form, as declared in a
+ *       hook_forms() implementation.
  *   - rebuild_info: Internal. Similar to 'build_info', but pertaining to
  *     drupal_rebuild_form().
  *   - rebuild: Normally, after the entire form processing is completed and
@@ -3349,6 +3351,13 @@ function form_process_machine_name($element, &$form_state) {
     'replace' => '_',
   );
 
+  // By default, machine names are restricted to Latin alphanumeric characters.
+  // So, default to LTR directionality.
+  if (!isset($element['#attributes'])) {
+    $element['#attributes'] = array();
+  }
+  $element['#attributes'] += array('dir' => 'ltr');
+
   // The source element defaults to array('name'), but may have been overidden.
   if (empty($element['#machine_name']['source'])) {
     return $element;
@@ -3786,13 +3795,27 @@ function theme_password($variables) {
  * Expand weight elements into selects.
  */
 function form_process_weight($element) {
-  for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
-    $weights[$n] = $n;
-  }
-  $element['#options'] = $weights;
-  $element['#type'] = 'select';
   $element['#is_weight'] = TRUE;
-  $element += element_info('select');
+
+  // If the number of options is small enough, use a select field.
+  $max_elements = variable_get('drupal_weight_select_max', DRUPAL_WEIGHT_SELECT_MAX);
+  if ($element['#delta'] <= $max_elements) {
+    $element['#type'] = 'select';
+    for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
+      $weights[$n] = $n;
+    }
+    $element['#options'] = $weights;
+    $element += element_info('select');
+  }
+  // Otherwise, use a text field.
+  else {
+    $element['#type'] = 'textfield';
+    // Use a field big enough to fit most weights.
+    $element['#size'] = 10;
+    $element['#element_validate'] = array('element_validate_integer');
+    $element += element_info('textfield');
+  }
+
   return $element;
 }
 
@@ -3976,7 +3999,7 @@ function theme_form_element_label($variables) {
   $t = get_t();
 
   // If title and required marker are both empty, output no label.
-  if (empty($element['#title']) && empty($element['#required'])) {
+  if ((!isset($element['#title']) || $element['#title'] === '') && empty($element['#required'])) {
     return '';
   }
 
index 416fad6..7fcc57a 100644 (file)
@@ -143,4 +143,3 @@ function _drupal_depth_first_search(&$graph, &$state, $start, &$component = NULL
   // topological order if the graph is acyclic.
   $state['last_visit_order'][] = $start;
 }
-
index 8dc36b9..f6ae7f1 100644 (file)
@@ -186,14 +186,14 @@ function image_scale_and_crop(stdClass $image, $width, $height) {
  *   Dimensions to be modified - an array with components width and height, in
  *   pixels.
  * @param $width
- *   The target width, in pixels. This value is omitted then the scaling will
+ *   The target width, in pixels. If this value is NULL then the scaling will be
  *   based only on the height value.
  * @param $height
- *   The target height, in pixels. This value is omitted then the scaling will
- *   based only on the width value.
+ *   The target height, in pixels. If this value is NULL then the scaling will
+ *   be based only on the width value.
  * @param $upscale
- *   Boolean indicating that files smaller than the dimensions will be scaled
- *   up. This generally results in a low quality image.
+ *   Boolean indicating that images smaller than the target dimensions will be
+ *   scaled up. This generally results in a low quality image.
  *
  * @return
  *   TRUE if $dimensions was modified, FALSE otherwise.
@@ -203,31 +203,25 @@ function image_scale_and_crop(stdClass $image, $width, $height) {
 function image_dimensions_scale(array &$dimensions, $width = NULL, $height = NULL, $upscale = FALSE) {
   $aspect = $dimensions['height'] / $dimensions['width'];
 
-  if ($upscale) {
-    // Set width/height according to aspect ratio if either is empty.
-    $width = !empty($width) ? $width : $height / $aspect;
-    $height = !empty($height) ? $height : $width / $aspect;
+  // Calculate one of the dimensions from the other target dimension,
+  // ensuring the same aspect ratio as the source dimensions. If one of the
+  // target dimensions is missing, that is the one that is calculated. If both
+  // are specified then the dimension calculated is the one that would not be
+  // calculated to be bigger than its target.
+  if (($width && !$height) || ($width && $height && $aspect < $height / $width)) {
+    $height = (int) round($width * $aspect);
   }
   else {
-    // Set impossibly large values if the width and height aren't set.
-    $width = !empty($width) ? $width : 9999999;
-    $height = !empty($height) ? $height : 9999999;
-
-    // Don't scale up.
-    if (round($width) >= $dimensions['width'] && round($height) >= $dimensions['height']) {
-      return FALSE;
-    }
+    $width = (int) round($height / $aspect);
   }
 
-  if ($aspect < $height / $width) {
-    $dimensions['width'] = $width;
-    $dimensions['height'] = (int) round($width * $aspect);
-  }
-  else {
-    $dimensions['width'] = (int) round($height / $aspect);
-    $dimensions['height'] = $height;
+  // Don't upscale if the option isn't enabled.
+  if (!$upscale && ($width >= $dimensions['width'] || $height >= $dimensions['height'])) {
+    return FALSE;
   }
 
+  $dimensions['width'] = $width;
+  $dimensions['height'] = $height;
   return TRUE;
 }
 
index a74dfdf..ec3a853 100644 (file)
@@ -570,6 +570,12 @@ function install_tasks($install_state) {
 
   // Now add any tasks defined by the installation profile.
   if (!empty($install_state['parameters']['profile'])) {
+    // Load the profile install file, because it is not always loaded when
+    // hook_install_tasks() is invoked (e.g. batch processing).
+    $profile_install_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.install';
+    if (file_exists($profile_install_file)) {
+      include_once $profile_install_file;
+    }
     $function = $install_state['parameters']['profile'] . '_install_tasks';
     if (function_exists($function)) {
       $result = $function($install_state);
@@ -595,7 +601,7 @@ function install_tasks($install_state) {
   // Allow the installation profile to modify the full list of tasks.
   if (!empty($install_state['parameters']['profile'])) {
     $profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile';
-    if (is_file($profile_file)) {
+    if (file_exists($profile_file)) {
       include_once $profile_file;
       $function = $install_state['parameters']['profile'] . '_install_tasks_alter';
       if (function_exists($function)) {
@@ -710,8 +716,10 @@ function install_display_output($output, $install_state) {
  *
  * @return
  *   A themed status report, or an exception if there are requirement errors.
- *   Otherwise, no output is returned, so that the next task can be run
- *   in the same page request.
+ *   If there are only requirement warnings, a themed status report is shown
+ *   initially, but the user is allowed to bypass it by providing 'continue=1'
+ *   in the URL. Otherwise, no output is returned, so that the next task can be
+ *   run in the same page request.
  */
 function install_verify_requirements(&$install_state) {
   // Check the installation requirements for Drupal and this profile.
@@ -723,22 +731,30 @@ function install_verify_requirements(&$install_state) {
   // Check the severity of the requirements reported.
   $severity = drupal_requirements_severity($requirements);
 
-  if ($severity == REQUIREMENT_ERROR) {
+  // If there are errors, always display them. If there are only warnings, skip
+  // them if the user has provided a URL parameter acknowledging the warnings
+  // and indicating a desire to continue anyway. See drupal_requirements_url().
+  if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($install_state['parameters']['continue']))) {
     if ($install_state['interactive']) {
       drupal_set_title(st('Requirements problem'));
       $status_report = theme('status_report', array('requirements' => $requirements));
-      $status_report .= st('Check the error messages and <a href="!url">proceed with the installation</a>.', array('!url' => check_url(request_uri())));
+      $status_report .= st('Check the error messages and <a href="!url">proceed with the installation</a>.', array('!url' => check_url(drupal_requirements_url($severity))));
       return $status_report;
     }
     else {
-      // Throw an exception showing all unmet requirements.
+      // Throw an exception showing any unmet requirements.
       $failures = array();
       foreach ($requirements as $requirement) {
+        // Skip warnings altogether for non-interactive installations; these
+        // proceed in a single request so there is no good opportunity (and no
+        // good method) to warn the user anyway.
         if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
           $failures[] = $requirement['title'] . ': ' . $requirement['value'] . "\n\n" . $requirement['description'];
         }
       }
-      throw new Exception(implode("\n\n", $failures));
+      if (!empty($failures)) {
+        throw new Exception(implode("\n\n", $failures));
+      }
     }
   }
 }
@@ -1290,7 +1306,7 @@ function install_already_done_error() {
  */
 function install_load_profile(&$install_state) {
   $profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile';
-  if (is_file($profile_file)) {
+  if (file_exists($profile_file)) {
     include_once $profile_file;
     $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['locale']);
   }
@@ -1408,13 +1424,6 @@ function install_import_locales(&$install_state) {
  *   The form API definition for the site configuration form.
  */
 function install_configure_form($form, &$form_state, &$install_state) {
-  if (variable_get('site_name', FALSE) || variable_get('site_mail', FALSE)) {
-    // Site already configured: This should never happen, means re-running the
-    // installer, possibly by an attacker after the 'install_task' variable got
-    // accidentally blown somewhere. Stop it now.
-    throw new Exception(install_already_done_error());
-  }
-
   drupal_set_title(st('Configure site'));
 
   // Warn about settings.php permissions risk
@@ -1816,7 +1825,7 @@ function install_configure_form_submit($form, &$form_state) {
 
   // We precreated user 1 with placeholder values. Let's save the real values.
   $account = user_load(1);
-  $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1);
+  $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1, 'timezone' => $form_state['values']['date_default_timezone']);
   user_save($account, array_merge($form_state['values']['account'], $merge_data));
   // Load global $user and perform final login tasks.
   $user = user_load(1);
index 516e146..6411f8f 100644 (file)
@@ -999,7 +999,6 @@ function drupal_install_fix_file($file, $mask, $message = TRUE) {
   }
 }
 
-
 /**
  * Send the user to a different installer page.
  *
@@ -1017,6 +1016,68 @@ function install_goto($path) {
 }
 
 /**
+ * Returns the URL of the current script, with modified query parameters.
+ *
+ * This function can be called by low-level scripts (such as install.php and
+ * update.php) and returns the URL of the current script. Existing query
+ * parameters are preserved by default, but new ones can optionally be merged
+ * in.
+ *
+ * This function is used when the script must maintain certain query parameters
+ * over multiple page requests in order to work correctly. In such cases (for
+ * example, update.php, which requires the 'continue=1' parameter to remain in
+ * the URL throughout the update process if there are any requirement warnings
+ * that need to be bypassed), using this function to generate the URL for links
+ * to the next steps of the script ensures that the links will work correctly.
+ *
+ * @param $query
+ *   (optional) An array of query parameters to merge in to the existing ones.
+ *
+ * @return
+ *   The URL of the current script, with query parameters modified by the
+ *   passed-in $query. The URL is not sanitized, so it still needs to be run
+ *   through check_url() if it will be used as an HTML attribute value.
+ *
+ * @see drupal_requirements_url()
+ */
+function drupal_current_script_url($query = array()) {
+  $uri = $_SERVER['SCRIPT_NAME'];
+  $query = array_merge(drupal_get_query_parameters(), $query);
+  if (!empty($query)) {
+    $uri .= '?' . drupal_http_build_query($query);
+  }
+  return $uri;
+}
+
+/**
+ * Returns a URL for proceeding to the next page after a requirements problem.
+ *
+ * This function can be called by low-level scripts (such as install.php and
+ * update.php) and returns a URL that can be used to attempt to proceed to the
+ * next step of the script.
+ *
+ * @param $severity
+ *   The severity of the requirements problem, as returned by
+ *   drupal_requirements_severity().
+ *
+ * @return
+ *   A URL for attempting to proceed to the next step of the script. The URL is
+ *   not sanitized, so it still needs to be run through check_url() if it will
+ *   be used as an HTML attribute value.
+ *
+ * @see drupal_current_script_url()
+ */
+function drupal_requirements_url($severity) {
+  $query = array();
+  // If there are no errors, only warnings, append 'continue=1' to the URL so
+  // the user can bypass this screen on the next page load.
+  if ($severity == REQUIREMENT_WARNING) {
+    $query['continue'] = 1;
+  }
+  return drupal_current_script_url($query);
+}
+
+/**
  * Functional equivalent of t(), used when some systems are not available.
  *
  * Used during the install process, when database, theme, and localization
index dabbefd..a88de57 100644 (file)
@@ -74,6 +74,7 @@ function _country_get_predefined_list() {
     'CO' => $t('Colombia'),
     'CR' => $t('Costa Rica'),
     'CU' => $t('Cuba'),
+    'CW' => $t('Curaçao'),
     'CV' => $t('Cape Verde'),
     'CX' => $t('Christmas Island'),
     'CY' => $t('Cyprus'),
diff --git a/includes/json-encode.inc b/includes/json-encode.inc
new file mode 100644 (file)
index 0000000..1efd6dd
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Provides a helper to properly encode HTML-safe JSON prior to PHP 5.3.0.
+ */
+
+/**
+ * Encodes a PHP variable to HTML-safe JSON for PHP versions below 5.3.0.
+ *
+ * @see drupal_json_encode()
+ */
+function drupal_json_encode_helper($var) {
+  switch (gettype($var)) {
+    case 'boolean':
+      return $var ? 'true' : 'false'; // Lowercase necessary!
+
+    case 'integer':
+    case 'double':
+      return $var;
+
+    case 'resource':
+    case 'string':
+      // Always use Unicode escape sequences (\u0022) over JSON escape
+      // sequences (\") to prevent browsers interpreting these as
+      // special characters.
+      $replace_pairs = array(
+        // ", \ and U+0000 - U+001F must be escaped according to RFC 4627.
+        '\\' => '\u005C',
+        '"' => '\u0022',
+        "\x00" => '\u0000',
+        "\x01" => '\u0001',
+        "\x02" => '\u0002',
+        "\x03" => '\u0003',
+        "\x04" => '\u0004',
+        "\x05" => '\u0005',
+        "\x06" => '\u0006',
+        "\x07" => '\u0007',
+        "\x08" => '\u0008',
+        "\x09" => '\u0009',
+        "\x0a" => '\u000A',
+        "\x0b" => '\u000B',
+        "\x0c" => '\u000C',
+        "\x0d" => '\u000D',
+        "\x0e" => '\u000E',
+        "\x0f" => '\u000F',
+        "\x10" => '\u0010',
+        "\x11" => '\u0011',
+        "\x12" => '\u0012',
+        "\x13" => '\u0013',
+        "\x14" => '\u0014',
+        "\x15" => '\u0015',
+        "\x16" => '\u0016',
+        "\x17" => '\u0017',
+        "\x18" => '\u0018',
+        "\x19" => '\u0019',
+        "\x1a" => '\u001A',
+        "\x1b" => '\u001B',
+        "\x1c" => '\u001C',
+        "\x1d" => '\u001D',
+        "\x1e" => '\u001E',
+        "\x1f" => '\u001F',
+        // Prevent browsers from interpreting these as as special.
+        "'" => '\u0027',
+        '<' => '\u003C',
+        '>' => '\u003E',
+        '&' => '\u0026',
+        // Prevent browsers from interpreting the solidus as special and
+        // non-compliant JSON parsers from interpreting // as a comment.
+        '/' => '\u002F',
+        // While these are allowed unescaped according to ECMA-262, section
+        // 15.12.2, they cause problems in some JSON parsers.
+        "\xe2\x80\xa8" => '\u2028', // U+2028, Line Separator.
+        "\xe2\x80\xa9" => '\u2029', // U+2029, Paragraph Separator.
+      );
+
+      return '"' . strtr($var, $replace_pairs) . '"';
+
+    case 'array':
+      // Arrays in JSON can't be associative. If the array is empty or if it
+      // has sequential whole number keys starting with 0, it's not associative
+      // so we can go ahead and convert it as an array.
+      if (empty($var) || array_keys($var) === range(0, sizeof($var) - 1)) {
+        $output = array();
+        foreach ($var as $v) {
+          $output[] = drupal_json_encode_helper($v);
+        }
+        return '[ ' . implode(', ', $output) . ' ]';
+      }
+      // Otherwise, fall through to convert the array as an object.
+
+    case 'object':
+      $output = array();
+      foreach ($var as $k => $v) {
+        $output[] = drupal_json_encode_helper(strval($k)) . ':' . drupal_json_encode_helper($v);
+      }
+      return '{' . implode(', ', $output) . '}';
+
+    default:
+      return 'null';
+  }
+}
index 6ebb897..0db4d4a 100644 (file)
@@ -43,6 +43,36 @@ define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session');
 define('LOCALE_JS_STRING', '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+');
 
 /**
+ * Regular expression pattern used to match simple JS object literal.
+ *
+ * This pattern matches a basic JS object, but will fail on an object with
+ * nested objects. Used in JS file parsing for string arg processing.
+ */
+define('LOCALE_JS_OBJECT', '\{.*?\}');
+
+/**
+ * Regular expression to match an object containing a key 'context'.
+ *
+ * Pattern to match a JS object containing a 'context key' with a string value,
+ * which is captured. Will fail if there are nested objects.
+ */
+define('LOCALE_JS_OBJECT_CONTEXT', '
+  \{              # match object literal start
+  .*?             # match anything, non-greedy
+  (?:             # match a form of "context"
+    \'context\'
+    |
+    "context"
+    |
+    context
+  )
+  \s*:\s*         # match key-value separator ":"
+  (' . LOCALE_JS_STRING . ')  # match context string
+  .*?             # match anything, non-greedy
+  \}              # match end of object literal
+');
+
+/**
  * Translation import mode overwriting all existing translations
  * if new translated version available.
  */
@@ -1447,6 +1477,9 @@ function _locale_parse_js_file($filepath) {
     [^\w]Drupal\s*\.\s*t\s*                       # match "Drupal.t" with whitespace
     \(\s*                                         # match "(" argument list start
     (' . LOCALE_JS_STRING . ')\s*                 # capture string argument
+    (?:,\s*' . LOCALE_JS_OBJECT . '\s*            # optionally capture str args
+      (?:,\s*' . LOCALE_JS_OBJECT_CONTEXT . '\s*) # optionally capture context
+    ?)?                                           # close optional args
     [,\)]                                         # match ")" or "," to finish
     ~sx', $file, $t_matches);
 
@@ -1474,55 +1507,73 @@ function _locale_parse_js_file($filepath) {
         (?:\s*\+\s*)?             # match "+" with possible whitespace, for str concat
       )+                          # match multiple because we supports concatenating strs
     )\s*                          # end capturing of plural string argument
+    (?:,\s*' . LOCALE_JS_OBJECT . '\s*          # optionally capture string args
+      (?:,\s*' . LOCALE_JS_OBJECT_CONTEXT . '\s*)?  # optionally capture context
+    )?
     [,\)]
     ~sx', $file, $plural_matches);
 
+  $matches = array();
 
-  // Loop through all matches and process them.
-  $all_matches = array_merge($plural_matches[1], $t_matches[1]);
-  foreach ($all_matches as $key => $string) {
-    $strings = array($string);
+  // Add strings from Drupal.t().
+  foreach ($t_matches[1] as $key => $string) {
+    $matches[] = array(
+      'string'  => $string,
+      'context' => $t_matches[2][$key],
+    );
+  }
+
+  // Add string from Drupal.formatPlural().
+  foreach ($plural_matches[1] as $key => $string) {
+    $matches[] = array(
+      'string'  => $string,
+      'context' => $plural_matches[3][$key],
+    );
 
     // If there is also a plural version of this string, add it to the strings array.
     if (isset($plural_matches[2][$key])) {
-      $strings[] = $plural_matches[2][$key];
-    }
-
-    foreach ($strings as $key => $string) {
-      // Remove the quotes and string concatenations from the string.
-      $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($string, 1, -1)));
-
-      $source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source AND textgroup = 'default'", array(':source' => $string))->fetchObject();
-      if ($source) {
-        // We already have this source string and now have to add the location
-        // to the location column, if this file is not yet present in there.
-        $locations = preg_split('~\s*;\s*~', $source->location);
-
-        if (!in_array($filepath, $locations)) {
-          $locations[] = $filepath;
-          $locations = implode('; ', $locations);
-
-          // Save the new locations string to the database.
-          db_update('locales_source')
-            ->fields(array(
-              'location' => $locations,
-            ))
-            ->condition('lid', $source->lid)
-            ->execute();
-        }
-      }
-      else {
-        // We don't have the source string yet, thus we insert it into the database.
-        db_insert('locales_source')
+      $matches[] = array(
+        'string'  => $plural_matches[2][$key],
+        'context' => $plural_matches[3][$key],
+      );
+    }
+  }
+
+  foreach ($matches as $key => $match) {
+    // Remove the quotes and string concatenations from the string.
+    $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['string'], 1, -1)));
+    $context = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['context'], 1, -1)));
+
+    $source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = 'default'", array(':source' => $string, ':context' => $context))->fetchObject();
+    if ($source) {
+      // We already have this source string and now have to add the location
+      // to the location column, if this file is not yet present in there.
+      $locations = preg_split('~\s*;\s*~', $source->location);
+
+      if (!in_array($filepath, $locations)) {
+        $locations[] = $filepath;
+        $locations = implode('; ', $locations);
+
+        // Save the new locations string to the database.
+        db_update('locales_source')
           ->fields(array(
-            'location' => $filepath,
-            'source' => $string,
-            'context' => '',
-            'textgroup' => 'default',
+            'location' => $locations,
           ))
+          ->condition('lid', $source->lid)
           ->execute();
       }
     }
+    else {
+      // We don't have the source string yet, thus we insert it into the database.
+      db_insert('locales_source')
+        ->fields(array(
+          'location' => $filepath,
+          'source' => $string,
+          'context' => $context,
+          'textgroup' => 'default',
+        ))
+        ->execute();
+    }
   }
 }
 
@@ -1942,11 +1993,11 @@ function _locale_rebuild_js($langcode = NULL) {
 
   // Construct the array for JavaScript translations.
   // Only add strings with a translation to the translations array.
-  $result = db_query("SELECT s.lid, s.source, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default'));
+  $result = db_query("SELECT s.lid, s.source, s.context, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default'));
 
   $translations = array();
   foreach ($result as $data) {
-    $translations[$data->source] = $data->translation;
+    $translations[$data->context][$data->source] = $data->translation;
   }
 
   // Construct the JavaScript file, if there are translations.
@@ -2242,7 +2293,7 @@ function _locale_batch_import($filepath, &$context) {
   // The filename is either {langcode}.po or {prefix}.{langcode}.po, so
   // we can extract the language code to use for the import from the end.
   if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) {
-    $file = (object) array('filename' => basename($filepath), 'uri' => $filepath);
+    $file = (object) array('filename' => drupal_basename($filepath), 'uri' => $filepath);
     _locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode[2]);
     $context['results'][] = $filepath;
   }
index 7272df9..13a6f46 100644 (file)
@@ -57,6 +57,12 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
  *     user_mail_tokens($variables, $data, $options);
  *     switch($key) {
  *       case 'notice':
+ *         // If the recipient can receive such notices by instant-message, do
+ *         // not send by email.
+ *         if (example_im_send($key, $message, $params)) {
+ *           $message['send'] = FALSE;
+ *           break;
+ *         }
  *         $langcode = $message['language']->language;
  *         $message['subject'] = t('Notification from !site', $variables, array('langcode' => $langcode));
  *         $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, array('langcode' => $langcode));
@@ -65,6 +71,19 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
  *   }
  * @endcode
  *
+ * Another example, which uses drupal_mail() to format a message for sending
+ * later:
+ *
+ * @code
+ *   $params = array('current_conditions' => $data);
+ *   $to = 'user@example.com';
+ *   $message = drupal_mail('example', 'notice', $to, $language, $params, FALSE);
+ *   // Only add to the spool if sending was not canceled.
+ *   if ($message['send']) {
+ *     example_spool_message($message);
+ *   }
+ * @endcode
+ *
  * @param $module
  *   A module name to invoke hook_mail() on. The {$module}_mail() hook will be
  *   called to complete the $message structure which will already contain common
@@ -86,8 +105,10 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
  * @param $from
  *   Sets From to this value, if given.
  * @param $send
- *   Send the message directly, without calling drupal_mail_system()->mail()
- *   manually.
+ *   If TRUE, drupal_mail() will call drupal_mail_system()->mail() to deliver
+ *   the message, and store the result in $message['result']. Modules
+ *   implementing hook_mail_alter() may cancel sending by setting
+ *   $message['send'] to FALSE.
  *
  * @return
  *   The $message array structure containing all details of the
@@ -108,6 +129,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
     'from'     => isset($from) ? $from : $default_from,
     'language' => $language,
     'params'   => $params,
+    'send'     => TRUE,
     'subject'  => '',
     'body'     => array()
   );
@@ -148,12 +170,20 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
 
   // Optionally send e-mail.
   if ($send) {
-    $message['result'] = $system->mail($message);
-
-    // Log errors
-    if (!$message['result']) {
-      watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
-      drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
+    // The original caller requested sending. Sending was canceled by one or
+    // more hook_mail_alter() implementations. We set 'result' to NULL, because
+    // FALSE indicates an error in sending.
+    if (empty($message['send'])) {
+      $message['result'] = NULL;
+    }
+    // Sending was originally requested and was not canceled.
+    else {
+      $message['result'] = $system->mail($message);
+      // Log errors.
+      if (!$message['result']) {
+        watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
+        drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
+      }
     }
   }
 
index dad65d7..25a87af 100644 (file)
@@ -275,6 +275,20 @@ define('MENU_MAX_DEPTH', 9);
  */
 
 /**
+ * Reserved key to identify the most specific menu link for a given path.
+ *
+ * The value of this constant is a hash of the constant name. We use the hash
+ * so that the reserved key is over 32 characters in length and will not
+ * collide with allowed menu names:
+ * @code
+ * sha1('MENU_PREFERRED_LINK') = 1cf698d64d1aa4b83907cf6ed55db3a7f8e92c91
+ * @endcode
+ *
+ * @see menu_link_get_preferred()
+ */
+define('MENU_PREFERRED_LINK', '1cf698d64d1aa4b83907cf6ed55db3a7f8e92c91');
+
+/**
  * Returns the ancestors (and relevant placeholders) for any given path.
  *
  * For example, the ancestors of node/12345/edit are:
@@ -1241,7 +1255,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
         if ($item['access']) {
           // Find a menu link corresponding to the current path. If $active_path
           // is NULL, let menu_link_get_preferred() determine the path.
-          if ($active_link = menu_link_get_preferred($active_path)) {
+          if ($active_link = menu_link_get_preferred($active_path, $menu_name)) {
             // The active link may only be taken into account to build the
             // active trail, if it resides in the requested menu. Otherwise,
             // we'd needlessly re-run _menu_build_tree() queries for every menu
@@ -1325,6 +1339,8 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
  *     Defaults to 1, which is the default to build a whole tree for a menu, i.e.
  *     excluding menu container itself.
  *   - max_depth: The maximum depth of menu links in the resulting tree.
+ *   - conditions: An associative array of custom database select query
+ *     condition key/value pairs; see _menu_build_tree() for the actual query.
  *
  * @return
  *   A fully built menu tree.
@@ -1408,6 +1424,12 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
     if (isset($parameters['max_depth'])) {
       $query->condition('ml.depth', $parameters['max_depth'], '<=');
     }
+    // Add custom query conditions, if any were passed.
+    if (isset($parameters['conditions'])) {
+      foreach ($parameters['conditions'] as $column => $value) {
+        $query->condition($column, $value);
+      }
+    }
 
     // Build an ordered array of links using the query result object.
     $links = array();
@@ -2260,6 +2282,13 @@ function theme_menu_local_tasks(&$variables) {
 
 /**
  * Set (or get) the active menu for the current page - determines the active trail.
+ *
+ * @return
+ *   An array of menu machine names, in order of preference. The
+ *   'menu_default_active_menus' variable may be used to assert a menu order
+ *   different from the order of creation, or to prevent a particular menu from
+ *   being used at all in the active trail.
+ *   E.g., $conf['menu_default_active_menus'] = array('navigation', 'main-menu')
  */
 function menu_set_active_menu_names($menu_names = NULL) {
   $active = &drupal_static(__FUNCTION__);
@@ -2390,23 +2419,30 @@ function menu_set_active_trail($new_trail = NULL) {
  * @param $path
  *   The path, for example 'node/5'. The function will find the corresponding
  *   menu link ('node/5' if it exists, or fallback to 'node/%').
+ * @param $selected_menu
+ *   The name of a menu used to restrict the search for a preferred menu link.
+ *   If not specified, all the menus returned by menu_get_active_menu_names()
+ *   will be used.
  *
  * @return
- *   A fully translated menu link, or NULL if no matching menu link was
+ *   A fully translated menu link, or FALSE if no matching menu link was
  *   found. The most specific menu link ('node/5' preferred over 'node/%') in
  *   the most preferred menu (as defined by menu_get_active_menu_names()) is
  *   returned.
  */
-function menu_link_get_preferred($path = NULL) {
+function menu_link_get_preferred($path = NULL, $selected_menu = NULL) {
   $preferred_links = &drupal_static(__FUNCTION__);
 
   if (!isset($path)) {
     $path = $_GET['q'];
   }
 
-  if (!isset($preferred_links[$path])) {
-    $preferred_links[$path] = FALSE;
+  if (empty($selected_menu)) {
+    // Use an illegal menu name as the key for the preferred menu link.
+    $selected_menu = MENU_PREFERRED_LINK;
+  }
 
+  if (!isset($preferred_links[$path])) {
     // Look for the correct menu link by building a list of candidate paths,
     // which are ordered by priority (translated hrefs are preferred over
     // untranslated paths). Afterwards, the most relevant path is picked from
@@ -2428,6 +2464,8 @@ function menu_link_get_preferred($path = NULL) {
 
     // Retrieve a list of menu names, ordered by preference.
     $menu_names = menu_get_active_menu_names();
+    // Put the selected menu at the front of the list.
+    array_unshift($menu_names, $selected_menu);
 
     $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
     $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
@@ -2435,7 +2473,6 @@ function menu_link_get_preferred($path = NULL) {
     // Weight must be taken from {menu_links}, not {menu_router}.
     $query->addField('ml', 'weight', 'link_weight');
     $query->fields('m');
-    $query->condition('ml.menu_name', $menu_names, 'IN');
     $query->condition('ml.link_path', $path_candidates, 'IN');
 
     // Sort candidates by link path and menu name.
@@ -2443,29 +2480,35 @@ function menu_link_get_preferred($path = NULL) {
     foreach ($query->execute() as $candidate) {
       $candidate['weight'] = $candidate['link_weight'];
       $candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
+      // Add any menus not already in the menu name search list.
+      if (!in_array($candidate['menu_name'], $menu_names)) {
+        $menu_names[] = $candidate['menu_name'];
+      }
     }
 
-    // Pick the most specific link, in the most preferred menu.
+    // Store the most specific link for each menu. Also save the most specific
+    // link of the most preferred menu in $preferred_link.
     foreach ($path_candidates as $link_path) {
-      if (!isset($candidates[$link_path])) {
-        continue;
-      }
-      foreach ($menu_names as $menu_name) {
-        if (!isset($candidates[$link_path][$menu_name])) {
-          continue;
-        }
-        $candidate_item = $candidates[$link_path][$menu_name];
-        $map = explode('/', $path);
-        _menu_translate($candidate_item, $map);
-        if ($candidate_item['access']) {
-          $preferred_links[$path] = $candidate_item;
+      if (isset($candidates[$link_path])) {
+        foreach ($menu_names as $menu_name) {
+          if (empty($preferred_links[$path][$menu_name]) && isset($candidates[$link_path][$menu_name])) {
+            $candidate_item = $candidates[$link_path][$menu_name];
+            $map = explode('/', $path);
+            _menu_translate($candidate_item, $map);
+            if ($candidate_item['access']) {
+              $preferred_links[$path][$menu_name] = $candidate_item;
+              if (empty($preferred_links[$path][MENU_PREFERRED_LINK])) {
+                // Store the most specific link.
+                $preferred_links[$path][MENU_PREFERRED_LINK] = $candidate_item;
+              }
+            }
+          }
         }
-        break 2;
       }
     }
   }
 
-  return $preferred_links[$path];
+  return isset($preferred_links[$path][$selected_menu]) ? $preferred_links[$path][$selected_menu] : FALSE;
 }
 
 /**
index 66c77f5..77e35b7 100644 (file)
@@ -539,6 +539,7 @@ function module_disable($module_list, $disable_dependents = TRUE) {
     system_list_reset();
     module_list(TRUE);
     module_implements('', FALSE, TRUE);
+    entity_info_cache_clear();
     // Invoke hook_modules_disabled before disabling modules,
     // so we can still call module hooks to get information.
     module_invoke_all('modules_disabled', $invoke_modules);
@@ -952,10 +953,24 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
       }
       // If any modules implement one of the extra hooks that do not implement
       // the primary hook, we need to add them to the $modules array in their
-      // appropriate order.
+      // appropriate order. module_implements() can only return ordered
+      // implementations of a single hook. To get the ordered implementations
+      // of multiple hooks, we mimic the module_implements() logic of first
+      // ordering by module_list(), and then calling
+      // drupal_alter('module_implements').
       if (array_diff($extra_modules, $modules)) {
-        // Order the modules by the order returned by module_list().
+        // Merge the arrays and order by module_list().
         $modules = array_intersect(module_list(), array_merge($modules, $extra_modules));
+        // Since module_implements() already took care of loading the necessary
+        // include files, we can safely pass FALSE for the array values.
+        $implementations = array_fill_keys($modules, FALSE);
+        // Let modules adjust the order solely based on the primary hook. This
+        // ensures the same module order regardless of whether this if block
+        // runs. Calling drupal_alter() recursively in this way does not result
+        // in an infinite loop, because this call is for a single $type, so we
+        // won't end up in this code block again.
+        drupal_alter('module_implements', $implementations, $hook);
+        $modules = array_keys($implementations);
       }
       foreach ($modules as $module) {
         // Since $modules is a merged array, for any given module, we do not
@@ -1003,4 +1018,3 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
     $function($data, $context1, $context2);
   }
 }
-
index c0761da..d4f5f73 100644 (file)
@@ -285,4 +285,3 @@ function user_needs_new_hash($account) {
   // Check whether the iteration count used differs from the standard number.
   return (_password_get_count_log2($account->pass) !== $count_log2);
 }
-
index db60537..ed5b639 100644 (file)
  * Initialize the $_GET['q'] variable to the proper normal path.
  */
 function drupal_path_initialize() {
-  if (!empty($_GET['q'])) {
-    $_GET['q'] = drupal_get_normal_path($_GET['q']);
-  }
-  else {
-    $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
+  // Ensure $_GET['q'] is set before calling drupal_normal_path(), to support
+  // path caching with hook_url_inbound_alter().
+  if (empty($_GET['q'])) {
+    $_GET['q'] = variable_get('site_frontpage', 'node');
   }
+  $_GET['q'] = drupal_get_normal_path($_GET['q']);
 }
 
 /**
index 3fb14fb..c7b8702 100644 (file)
@@ -183,4 +183,3 @@ function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
 /**
  * @} End of "defgroup registry".
  */
-
index e47668e..2af8c9e 100644 (file)
@@ -317,7 +317,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
     }
 
     $extension = '';
-    $file_parts = explode('.', basename($uri));
+    $file_parts = explode('.', drupal_basename($uri));
 
     // Remove the first part: a full filename should not match an extension.
     array_shift($file_parts);
@@ -377,7 +377,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
     $realpath = realpath($path);
     if (!$realpath) {
       // This file does not yet exist.
-      $realpath = realpath(dirname($path)) . '/' . basename($path);
+      $realpath = realpath(dirname($path)) . '/' . drupal_basename($path);
     }
     $directory = realpath($this->getDirectoryPath());
     if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
index 252eec0..da4200e 100644 (file)
@@ -237,18 +237,33 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
 /**
  * Get the theme registry.
  *
+ * @param $complete
+ *   Optional boolean to indicate whether to return the complete theme registry
+ *   array or an instance of the ThemeRegistry class. If TRUE, the complete
+ *   theme registry array will be returned. This is useful if you want to
+ *   foreach over the whole registry, use array_* functions or inspect it in a
+ *   debugger. If FALSE, an instance of the ThemeRegistry class will be
+ *   returned, this provides an ArrayObject which allows it to be accessed
+ *   with array syntax and  isset(), and should be more lightweight
+ *   than the full registry. Defaults to TRUE.
+ *
  * @return
- *   The theme registry array if it has been stored in memory, NULL otherwise.
+ *   The complete theme registry array, or an instance of the ThemeRegistry
+ *   class.
  */
-function theme_get_registry() {
-  static $theme_registry = NULL;
+function theme_get_registry($complete = TRUE) {
+  static $theme_registry = array();
+  $key = (int) $complete;
 
-  if (!isset($theme_registry)) {
+  if (!isset($theme_registry[$key])) {
     list($callback, $arguments) = _theme_registry_callback();
-    $theme_registry = call_user_func_array($callback, $arguments);
+    if (!$complete) {
+      $arguments[] = FALSE;
+    }
+    $theme_registry[$key] = call_user_func_array($callback, $arguments);
   }
 
-  return $theme_registry;
+  return $theme_registry[$key];
 }
 
 /**
@@ -268,7 +283,7 @@ function _theme_registry_callback($callback = NULL, array $arguments = array())
 }
 
 /**
- * Get the theme_registry cache from the database; if it doesn't exist, build it.
+ * Get the theme_registry cache; if it doesn't exist, build it.
  *
  * @param $theme
  *   The loaded $theme object as returned by list_themes().
@@ -277,23 +292,34 @@ function _theme_registry_callback($callback = NULL, array $arguments = array())
  *   oldest first order.
  * @param $theme_engine
  *   The name of the theme engine.
+ * @param $complete
+ *   Whether to load the complete theme registry or an instance of the
+ *   ThemeRegistry class.
+ *
+ * @return
+ *   The theme registry array, or an instance of the ThemeRegistry class.
  */
-function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
-  // Check the theme registry cache; if it exists, use it.
-  $cache = cache_get("theme_registry:$theme->name", 'cache');
-  if (isset($cache->data)) {
-    $registry = $cache->data;
+function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $complete = TRUE) {
+  if ($complete) {
+    // Check the theme registry cache; if it exists, use it.
+    $cached = cache_get("theme_registry:$theme->name");
+    if (isset($cached->data)) {
+      $registry = $cached->data;
+    }
+    else {
+      // If not, build one and cache it.
+      $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
+      // Only persist this registry if all modules are loaded. This assures a
+      // complete set of theme hooks.
+      if (module_load_all(NULL)) {
+        _theme_save_registry($theme, $registry);
+      }
+    }
+    return $registry;
   }
   else {
-    // If not, build one and cache it.
-    $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
-   // Only persist this registry if all modules are loaded. This assures a
-   // complete set of theme hooks.
-    if (module_load_all(NULL)) {
-      _theme_save_registry($theme, $registry);
-    }
+    return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache');
   }
-  return $registry;
 }
 
 /**
@@ -313,6 +339,116 @@ function drupal_theme_rebuild() {
 }
 
 /**
+ * Builds the run-time theme registry.
+ *
+ * Extends DrupalCacheArray to allow the theme registry to be accessed as a
+ * complete registry, while internally caching only the parts of the registry
+ * that are actually in use on the site. On cache misses the complete
+ * theme registry is loaded and used to update the run-time cache.
+ */
+class ThemeRegistry Extends DrupalCacheArray {
+
+  /**
+   * Whether the partial registry can be persisted to the cache.
+   *
+   * This is only allowed if all modules and the request method is GET. theme()
+   * should be very rarely called on POST requests and this avoids polluting
+   * the runtime cache.
+   */
+  protected $persistable;
+
+  /**
+   * The complete theme registry array.
+   */
+  protected $completeRegistry;
+
+  function __construct($cid, $bin) {
+    $this->cid = $cid;
+    $this->bin = $bin;
+    $this->persistable = module_load_all(NULL) && $_SERVER['REQUEST_METHOD'] == 'GET';
+
+    $data = array();
+    if ($this->persistable && $cached = cache_get($this->cid, $this->bin)) {
+      $data = $cached->data;
+    }
+    else {
+      // If there is no runtime cache stored, fetch the full theme registry,
+      // but then initialize each value to NULL. This allows offsetExists()
+      // to function correctly on non-registered theme hooks without triggering
+      // a call to resolveCacheMiss().
+      $data = $this->initializeRegistry();
+      if ($this->persistable) {
+        $this->set($data);
+      }
+    }
+    $this->storage = $data;
+  }
+
+  /**
+   * Initializes the full theme registry.
+   *
+   * @return
+   *   An array with the keys of the full theme registry, but the values
+   *   initialized to NULL.
+   */
+  function initializeRegistry() {
+    $this->completeRegistry = theme_get_registry();
+
+    return array_fill_keys(array_keys($this->completeRegistry), NULL);
+  }
+
+  public function offsetExists($offset) {
+    // Since the theme registry allows for theme hooks to be requested that
+    // are not registered, just check the existence of the key in the registry.
+    // Use array_key_exists() here since a NULL value indicates that the theme
+    // hook exists but has not yet been requested.
+    return array_key_exists($offset, $this->storage);
+  }
+
+  public function offsetGet($offset) {
+    // If the offset is set but empty, it is a registered theme hook that has
+    // not yet been requested. Offsets that do not exist at all were not
+    // registered in hook_theme().
+    if (isset($this->storage[$offset])) {
+      return $this->storage[$offset];
+    }
+    elseif (array_key_exists($offset, $this->storage)) {
+      return $this->resolveCacheMiss($offset);
+    }
+  }
+
+  public function resolveCacheMiss($offset) {
+    if (!isset($this->completeRegistry)) {
+      $this->completeRegistry = theme_get_registry();
+    }
+    $this->storage[$offset] = $this->completeRegistry[$offset];
+    if ($this->persistable) {
+      $this->persist($offset);
+    }
+    return $this->storage[$offset];
+  }
+
+  public function set($data, $lock = TRUE) {
+    $lock_name = $this->cid . ':' . $this->bin;
+    if (!$lock || lock_acquire($lock_name)) {
+      if ($cached = cache_get($this->cid, $this->bin)) {
+        // Use array merge instead of union so that filled in values in $data
+        // overwrite empty values in the current cache.
+        $data = array_merge($cached->data, $data);
+      }
+      else {
+        $registry = $this->initializeRegistry();
+        $data = array_merge($registry, $data);
+      }
+      cache_set($this->cid, $data, $this->bin);
+      if ($lock) {
+        lock_release($lock_name);
+      }
+    }
+  }
+}
+
+/**
  * Process a single implementation of hook_theme().
  *
  * @param $cache
@@ -657,8 +793,9 @@ function list_themes($refresh = FALSE) {
  * Generates themed output.
  *
  * All requests for themed output must go through this function. It examines
- * the request and routes it to the appropriate theme function or template, by
- * checking the theme registry.
+ * the request and routes it to the appropriate
+ * @link themeable theme function or template @endlink, by checking the theme
+ * registry.
  *
  * The first argument to this function is the name of the theme hook. For
  * instance, to theme a table, the theme hook name is 'table'. By default, this
@@ -758,6 +895,8 @@ function list_themes($refresh = FALSE) {
  *
  * @return
  *   An HTML string representing the themed output.
+ *
+ * @see themeable
  */
 function theme($hook, $variables = array()) {
   static $hooks = NULL;
@@ -771,7 +910,7 @@ function theme($hook, $variables = array()) {
 
   if (!isset($hooks)) {
     drupal_theme_initialize();
-    $hooks = theme_get_registry();
+    $hooks = theme_get_registry(FALSE);
   }
 
   // If an array of hook candidates were passed, use the first one that has an
@@ -1391,7 +1530,7 @@ function theme_link($variables) {
  * @param $variables
  *   An associative array containing:
  *   - links: An associative array of links to be themed. The key for each link
- *     is used as its css class. Each link should be itself an array, with the
+ *     is used as its CSS class. Each link should be itself an array, with the
  *     following elements:
  *     - title: The link text.
  *     - href: The link URL. If omitted, the 'title' is shown as a plain text
@@ -1981,6 +2120,8 @@ function theme_username($variables) {
 /**
  * Returns HTML for a progress bar.
  *
+ * Note that the core Batch API uses this only for non-JavaScript batch jobs.
+ *
  * @param $variables
  *   An associative array containing:
  *   - percent: The percentage of the progress.
index edc8a96..0b05c68 100644 (file)
  *   Text with tokens replaced.
  */
 function token_replace($text, array $data = array(), array $options = array()) {
+  $text_tokens = token_scan($text);
+  if (empty($text_tokens)) {
+    return $text;
+  }
+
   $replacements = array();
-  foreach (token_scan($text) as $type => $tokens) {
+  foreach ($text_tokens as $type => $tokens) {
     $replacements += token_generate($type, $tokens, $data, $options);
     if (!empty($options['clear'])) {
       $replacements += array_fill_keys($tokens, '');
index 3a13108..41f33f4 100644 (file)
@@ -746,7 +746,7 @@ function update_fix_d7_requirements() {
     // Rename 'site_offline_message' variable to 'maintenance_mode_message'
     // and 'site_offline' variable to 'maintenance_mode'.
     // Old variable is removed in update for system.module, see
-    // system_update_7036().
+    // system_update_7072().
     if ($message = variable_get('site_offline_message', NULL)) {
       variable_set('maintenance_mode_message', $message);
     }
index 363c6eb..9801629 100644 (file)
@@ -141,7 +141,7 @@ class Updater {
       return FALSE;
     }
     foreach ($info_files as $info_file) {
-      if (drupal_substr($info_file->filename, 0, -5) == basename($directory)) {
+      if (drupal_substr($info_file->filename, 0, -5) == drupal_basename($directory)) {
         // Info file Has the same name as the directory, return it.
         return $info_file->uri;
       }
@@ -163,7 +163,7 @@ class Updater {
    *   The name of the project.
    */
   public static function getProjectName($directory) {
-    return basename($directory);
+    return drupal_basename($directory);
   }
 
   /**
index df6c48f..d195bff 100644 (file)
@@ -46,6 +46,13 @@ function drupal_var_export($var, $prefix = '') {
       $output = "'" . $var . "'";
     }
   }
+  elseif (is_object($var) && get_class($var) === 'stdClass') {
+    // var_export() will export stdClass objects using an undefined
+    // magic method __set_state() leaving the export broken. This
+    // workaround avoids this by casting the object as an array for
+    // export and casting it back to an object when evaluated.
+    $output .= '(object) ' . drupal_var_export((array) $var, $prefix);
+  }
   else {
     $output = var_export($var, TRUE);
   }
index 92e5d14..b1c6f39 100644 (file)
@@ -622,4 +622,3 @@ function xmlrpc_error_msg() {
 function xmlrpc_clear_error() {
   xmlrpc_error(NULL, NULL, TRUE);
 }
-
index 70c7cda..118f652 100644 (file)
@@ -382,4 +382,3 @@ function xmlrpc_server_method_help($method) {
   $xmlrpc_server = xmlrpc_server_get();
   return $xmlrpc_server->help[$method];
 }
-
index 830c8aa..900ca1d 100644 (file)
@@ -318,7 +318,7 @@ Drupal.ajax.prototype.beforeSerialize = function (element, options) {
 Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
   // This function is left empty to make it simple to override for modules
   // that wish to add functionality here.
-}
+};
 
 /**
  * Prepare the Ajax request before it is sent.
index 66b7897..d522a5b 100644 (file)
@@ -16,7 +16,6 @@ Drupal.behaviors.authorizeFileTransferForm = {
 
     // Removes the float on the select box (used for non-JS interface).
     if ($('.connection-settings-update-filetransfer-default-wrapper').length > 0) {
-      console.log($('.connection-settings-update-filetransfer-default-wrapper'));
       $('.connection-settings-update-filetransfer-default-wrapper').css('float', 'none');
     }
     // Hides the submit button for non-js users.
index 5e85be4..02a886c 100644 (file)
@@ -41,7 +41,7 @@ Drupal.autocompleteSubmit = function () {
 Drupal.jsAC = function ($input, db) {
   var ac = this;
   this.input = $input[0];
-  this.ariaLive = $('#' + $input.attr('id') + '-autocomplete-aria-live');
+  this.ariaLive = $('#' + this.input.id + '-autocomplete-aria-live');
   this.db = db;
 
   $input
index 7e2cc4d..83b0884 100644 (file)
@@ -164,7 +164,7 @@ Drupal.formatString = function(str, args) {
     str = str.replace(key, args[key]);
   }
   return str;
-}
+};
 
 /**
  * Translate strings to the page language or a given language.
@@ -177,13 +177,21 @@ Drupal.formatString = function(str, args) {
  *   An object of replacements pairs to make after translation. Incidences
  *   of any key in this array are replaced with the corresponding value.
  *   See Drupal.formatString().
+ *
+ * @param options
+ *   - 'context' (defaults to the empty context): The context the source string
+ *     belongs to.
+ *
  * @return
  *   The translated string.
  */
-Drupal.t = function (str, args) {
+Drupal.t = function (str, args, options) {
+  options = options || {};
+  options.context = options.context || '';
+
   // Fetch the localized version of the string.
-  if (Drupal.locale.strings && Drupal.locale.strings[str]) {
-    str = Drupal.locale.strings[str];
+  if (Drupal.locale.strings && Drupal.locale.strings[options.context] && Drupal.locale.strings[options.context][str]) {
+    str = Drupal.locale.strings[options.context][str];
   }
 
   if (args) {
@@ -216,25 +224,27 @@ Drupal.t = function (str, args) {
  *   See Drupal.formatString().
  *   Note that you do not need to include @count in this array.
  *   This replacement is done automatically for the plural case.
+ * @param options
+ *   The options to pass to the Drupal.t() function.
  * @return
  *   A translated string.
  */
-Drupal.formatPlural = function (count, singular, plural, args) {
+Drupal.formatPlural = function (count, singular, plural, args, options) {
   var args = args || {};
   args['@count'] = count;
   // Determine the index of the plural form.
   var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
 
   if (index == 0) {
-    return Drupal.t(singular, args);
+    return Drupal.t(singular, args, options);
   }
   else if (index == 1) {
-    return Drupal.t(plural, args);
+    return Drupal.t(plural, args, options);
   }
   else {
     args['@count[' + index + ']'] = args['@count'];
     delete args['@count'];
-    return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args);
+    return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options);
   }
 };
 
index 6a684bd..f9cfa55 100644 (file)
@@ -17,6 +17,8 @@
  *
  * @see template_preprocess()
  * @see template_preprocess_aggregator_feed_source()
+ *
+ * @ingroup themeable
  */
 ?>
 <div class="feed-source">
index e9ad1e0..74b2284 100644 (file)
@@ -16,6 +16,8 @@
  *
  * @see template_preprocess()
  * @see template_preprocess_aggregator_item()
+ *
+ * @ingroup themeable
  */
 ?>
 <div class="feed-item">
index fcd57c7..f9199a5 100644 (file)
@@ -13,6 +13,8 @@
  *
  * @see template_preprocess()
  * @see template_preprocess_aggregator_summary_item()
+ *
+ * @ingroup themeable
  */
 ?>
 <a href="<?php print $feed_url; ?>"><?php print $feed_title; ?></a>
index 0e2133a..4a0551d 100644 (file)
@@ -14,6 +14,8 @@
  *
  * @see template_preprocess()
  * @see template_preprocess_aggregator_summary_items()
+ *
+ * @ingroup themeable
  */
 ?>
 <h3><?php print $title; ?></h3>
index 80b9032..2fa51a7 100644 (file)
@@ -10,6 +10,8 @@
  *
  * @see template_preprocess()
  * @see template_preprocess_aggregator_wrapper()
+ *
+ * @ingroup themeable
  */
 ?>
 <div id="aggregator">
index 52af1a6..9f92a67 100644 (file)
@@ -53,7 +53,11 @@ function aggregator_view() {
 }
 
 /**
- * Form builder; Generate a form to add/edit feed sources.
+ * Form constructor for adding and editing feed sources.
+ *
+ * @param $feed
+ *   If editing a feed, the feed to edit as a PHP stdClass value; if adding a
+ *   new feed, NULL.
  *
  * @ingroup forms
  * @see aggregator_form_feed_validate()
@@ -129,7 +133,9 @@ function aggregator_form_feed($form, &$form_state, stdClass $feed = NULL) {
 }
 
 /**
- * Validate aggregator_form_feed() form submissions.
+ * Form validation handler for aggregator_form_feed().
+ *
+ * @see aggregator_form_feed_submit()
  */
 function aggregator_form_feed_validate($form, &$form_state) {
   if ($form_state['values']['op'] == t('Save')) {
@@ -156,8 +162,9 @@ function aggregator_form_feed_validate($form, &$form_state) {
 }
 
 /**
- * Process aggregator_form_feed() form submissions.
+ * Form submission handler for aggregator_form_feed().
  *
+ * @see aggregator_form_feed_validate()
  * @todo Add delete confirmation dialog.
  */
 function aggregator_form_feed_submit($form, &$form_state) {
@@ -198,6 +205,14 @@ function aggregator_form_feed_submit($form, &$form_state) {
   }
 }
 
+/**
+ * Deletes a feed.
+ *
+ * @param $feed
+ *   An associative array describing the feed to be cleared.
+ *
+ * @see aggregator_admin_remove_feed_submit()
+ */
 function aggregator_admin_remove_feed($form, $form_state, $feed) {
   return confirm_form(
     array(
@@ -215,10 +230,9 @@ function aggregator_admin_remove_feed($form, $form_state, $feed) {
 }
 
 /**
- * Remove all items from a feed and redirect to the overview page.
+ * Form submission handler for aggregator_admin_remove_feed().
  *
- * @param $feed
- *   An associative array describing the feed to be cleared.
+ * Removes all items from a feed and redirects to the overview page.
  */
 function aggregator_admin_remove_feed_submit($form, &$form_state) {
   aggregator_remove($form_state['values']['feed']);
@@ -226,7 +240,7 @@ function aggregator_admin_remove_feed_submit($form, &$form_state) {
 }
 
 /**
- * Form builder; Generate a form to import feeds from OPML.
+ * Form constructor for importing feeds from OPML.
  *
  * @ingroup forms
  * @see aggregator_form_opml_validate()
@@ -280,7 +294,9 @@ function aggregator_form_opml($form, &$form_state) {
 }
 
 /**
- * Validate aggregator_form_opml form submissions.
+ * Form validation handler for aggregator_form_opml().
+ *
+ * @see aggregator_form_opml_submit()
  */
 function aggregator_form_opml_validate($form, &$form_state) {
   // If both fields are empty or filled, cancel.
@@ -295,7 +311,9 @@ function aggregator_form_opml_validate($form, &$form_state) {
 }
 
 /**
- * Process aggregator_form_opml form submissions.
+ * Form submission handler for aggregator_form_opml().
+ *
+ * @see aggregator_form_opml_validate()
  */
 function aggregator_form_opml_submit($form, &$form_state) {
   $data = '';
@@ -347,7 +365,7 @@ function aggregator_form_opml_submit($form, &$form_state) {
 }
 
 /**
- * Parse an OPML file.
+ * Parses an OPML file.
  *
  * Feeds are recognized as <outline> elements with the attributes "text" and
  * "xmlurl" set.
@@ -357,9 +375,9 @@ function aggregator_form_opml_submit($form, &$form_state) {
  *
  * @return
  *   An array of feeds, each an associative array with a "title" and a "url"
- *   element, or NULL if the OPML document failed to be parsed. An empty
- *   array will be returned if the document is valid but contains no feeds, as
- *   some OPML documents do.
+ *   element, or NULL if the OPML document failed to be parsed. An empty array
+ *   will be returned if the document is valid but contains no feeds, as some
+ *   OPML documents do.
  */
 function _aggregator_parse_opml($opml) {
   $feeds = array();
@@ -394,8 +412,9 @@ function aggregator_admin_refresh_feed($feed) {
 }
 
 /**
- * Form builder; Configure the aggregator system.
+ * Form constructor for the aggregator system settings.
  *
+ * @see aggregator_admin_form_submit()
  * @ingroup forms
  */
 function aggregator_admin_form($form, $form_state) {
@@ -503,6 +522,9 @@ function aggregator_admin_form($form, $form_state) {
   return $form;
 }
 
+/**
+ * Form submission handler for aggregator_admin_form().
+ */
 function aggregator_admin_form_submit($form, &$form_state) {
   if (isset($form_state['values']['aggregator_processors'])) {
     $form_state['values']['aggregator_processors'] = array_filter($form_state['values']['aggregator_processors']);
@@ -511,7 +533,13 @@ function aggregator_admin_form_submit($form, &$form_state) {
 }
 
 /**
- * Form builder; Generate a form to add/edit/delete aggregator categories.
+ * Form constructor to add/edit/delete aggregator categories.
+ *
+ * @param $edit
+ *   An associative array containing:
+ *   - title: A string to use for the category title.
+ *   - description: A string to use for the category description.
+ *   - cid: The category ID.
  *
  * @ingroup forms
  * @see aggregator_form_category_validate()
@@ -539,7 +567,9 @@ function aggregator_form_category($form, &$form_state, $edit = array('title' =>
 }
 
 /**
- * Validate aggregator_form_feed form submissions.
+ * Form validation handler for aggregator_form_category().
+ *
+ * @see aggregator_form_category_submit()
  */
 function aggregator_form_category_validate($form, &$form_state) {
   if ($form_state['values']['op'] == t('Save')) {
@@ -557,8 +587,9 @@ function aggregator_form_category_validate($form, &$form_state) {
 }
 
 /**
- * Process aggregator_form_category form submissions.
+ * Form submission handler for aggregator_form_category().
  *
+ * @see aggregator_form_category_validate()
  * @todo Add delete confirmation dialog.
  */
 function aggregator_form_category_submit($form, &$form_state) {
index f31413c..0f708eb 100644 (file)
  */
 
 /**
- * Implement this hook to create an alternative fetcher for aggregator module.
+ * Create an alternative fetcher for aggregator.module.
  *
- * A fetcher downloads feed data to a Drupal site. The fetcher is called
- * at the first of the three aggregation stages: data is downloaded by the
- * active fetcher, it is converted to a common format by the active parser and
- * finally, it is passed to all active processors which manipulate or store the
- * data.
+ * A fetcher downloads feed data to a Drupal site. The fetcher is called at the
+ * first of the three aggregation stages: first, data is downloaded by the
+ * active fetcher; second, it is converted to a common format by the active
+ * parser; and finally, it is passed to all active processors, which manipulate
+ * or store the data.
  *
  * Modules that define this hook can be set as active fetcher on
  * admin/config/services/aggregator. Only one fetcher can be active at a time.
  *
  * @param $feed
- *   The $feed object that describes the resource to be downloaded.
- *   $feed->url contains the link to the feed. Download the data at the URL
- *   and expose it to other modules by attaching it to $feed->source_string.
+ *   A feed object representing the resource to be downloaded. $feed->url
+ *   contains the link to the feed. Download the data at the URL and expose it
+ *   to other modules by attaching it to $feed->source_string.
  *
  * @return
  *   TRUE if fetching was successful, FALSE otherwise.
@@ -41,8 +41,7 @@ function hook_aggregator_fetch($feed) {
 }
 
 /**
- * Implement this hook to expose the title and a short description of your
- * fetcher.
+ * Specify the title and short description of your fetcher.
  *
  * The title and the description provided are shown on
  * admin/config/services/aggregator among other places. Use as title the human
@@ -68,41 +67,37 @@ function hook_aggregator_fetch_info() {
 }
 
 /**
- * Implement this hook to create an alternative parser for aggregator module.
+ * Create an alternative parser for aggregator module.
  *
  * A parser converts feed item data to a common format. The parser is called
- * at the second of the three aggregation stages: data is downloaded by the
- * active fetcher, it is converted to a common format by the active parser and
- * finally, it is passed to all active processors which manipulate or store the
- * data.
+ * at the second of the three aggregation stages: first, data is downloaded
+ * by the active fetcher; second, it is converted to a common format by the
+ * active parser; and finally, it is passed to all active processors which
+ * manipulate or store the data.
  *
- * Modules that define this hook can be set as active parser on
+ * Modules that define this hook can be set as the active parser on
  * admin/config/services/aggregator. Only one parser can be active at a time.
  *
  * @param $feed
- *   The $feed object that describes the resource to be parsed.
- *   $feed->source_string contains the raw feed data as a string. Parse data
- *   from $feed->source_string and expose it to other modules as an array of
- *   data items on $feed->items.
- *
- *   Feed format:
- *   - $feed->description (string) - description of the feed
- *   - $feed->image (string) - image for the feed
- *   - $feed->etag (string) - value of feed's entity tag header field
- *   - $feed->modified (UNIX timestamp) - value of feed's last modified header
- *     field
- *   - $feed->items (Array) - array of feed items.
- *
- *   By convention, the common format for a single feed item is:
- *   $item[key-name] = value;
- *
- *   Recognized keys:
- *   TITLE (string) - the title of a feed item
- *   DESCRIPTION (string) - the description (body text) of a feed item
- *   TIMESTAMP (UNIX timestamp) - the feed item's published time as UNIX timestamp
- *   AUTHOR (string) - the feed item's author
- *   GUID (string) - RSS/Atom global unique identifier
- *   LINK (string) - the feed item's URL
+ *   An object describing the resource to be parsed. $feed->source_string
+ *   contains the raw feed data. The hook implementation should parse this data
+ *   and add the following properties to the $feed object:
+ *   - description: The human-readable description of the feed.
+ *   - link: A full URL that directly relates to the feed.
+ *   - image: An image URL used to display an image of the feed.
+ *   - etag: An entity tag from the HTTP header used for cache validation to
+ *     determine if the content has been changed.
+ *   - modified: The UNIX timestamp when the feed was last modified.
+ *   - items: An array of feed items. The common format for a single feed item
+ *     is an associative array containing:
+ *     - title: The human-readable title of the feed item.
+ *     - description: The full body text of the item or a summary.
+ *     - timestamp: The UNIX timestamp when the feed item was last published.
+ *     - author: The author of the feed item.
+ *     - guid: The global unique identifier (GUID) string that uniquely
+ *       identifies the item. If not available, the link is used to identify
+ *       the item.
+ *     - link: A full URL to the individual feed item.
  *
  * @return
  *   TRUE if parsing was successful, FALSE otherwise.
@@ -122,8 +117,7 @@ function hook_aggregator_parse($feed) {
 }
 
 /**
- * Implement this hook to expose the title and a short description of your
- * parser.
+ * Specify the title and short description of your parser.
  *
  * The title and the description provided are shown on
  * admin/config/services/aggregator among other places. Use as title the human
@@ -149,23 +143,23 @@ function hook_aggregator_parse_info() {
 }
 
 /**
- * Implement this hook to create a processor for aggregator module.
+ * Create a processor for aggregator.module.
  *
  * A processor acts on parsed feed data. Active processors are called at the
- * third and last of the aggregation stages: data is downloaded by the active
- * fetcher, it is converted to a common format by the active parser and
- * finally, it is passed to all active processors which manipulate or store the
- * data.
+ * third and last of the aggregation stages: first, data is downloaded by the
+ * active fetcher; second, it is converted to a common format by the active
+ * parser; and finally, it is passed to all active processors that manipulate or
+ * store the data.
  *
  * Modules that define this hook can be activated as processor on
  * admin/config/services/aggregator.
  *
  * @param $feed
- *   The $feed object that describes the resource to be processed. $feed->items
- *   contains an array of feed items downloaded and parsed at the parsing
- *   stage. See hook_aggregator_parse() for the basic format of a single item
- *   in the $feed->items array. For the exact format refer to the particular
- *   parser in use.
+ *   A feed object representing the resource to be processed. $feed->items
+ *   contains an array of feed items downloaded and parsed at the parsing stage.
+ *   See hook_aggregator_parse() for the basic format of a single item in the
+ *   $feed->items array. For the exact format refer to the particular parser in
+ *   use.
  *
  * @see hook_aggregator_process_info()
  * @see hook_aggregator_fetch()
@@ -180,17 +174,16 @@ function hook_aggregator_process($feed) {
 }
 
 /**
- * Implement this hook to expose the title and a short description of your
- * processor.
+ * Specify the title and short description of your processor.
  *
  * The title and the description provided are shown most importantly on
  * admin/config/services/aggregator. Use as title the natural name of the
- * processor and as description a brief (40 to 80 characters) explanation of
- * the functionality.
+ * processor and as description a brief (40 to 80 characters) explanation of the
+ * functionality.
  *
- * This hook is only called if your module implements
- * hook_aggregator_process(). If this hook is not implemented aggregator
- * will use your module's file name as title and there will be no description.
+ * This hook is only called if your module implements hook_aggregator_process().
+ * If this hook is not implemented aggregator will use your module's file name
+ * as title and there will be no description.
  *
  * @return
  *   An associative array defining a title and a description string.
@@ -207,8 +200,7 @@ function hook_aggregator_process_info($feed) {
 }
 
 /**
- * Implement this hook to remove stored data if a feed is being deleted or a
- * feed's items are being removed.
+ * Remove stored feed data.
  *
  * Aggregator calls this hook if either a feed is deleted or a user clicks on
  * "remove items".
index eafb61e..9319ad9 100644 (file)
@@ -266,7 +266,10 @@ function aggregator_menu() {
 }
 
 /**
- * Title callback for aggregatory category pages.
+ * Title callback: Returns a title for aggregatory category pages.
+ *
+ * @param $category
+ *   An aggregator category.
  *
  * @return
  *   An aggregator category title.
@@ -276,11 +279,11 @@ function _aggregator_category_title($category) {
 }
 
 /**
- * Find out whether there are any aggregator categories.
+ * Determines whether there are any aggregator categories.
  *
  * @return
- *   TRUE if there is at least one category and the user has access to them, FALSE
- *   otherwise.
+ *   TRUE if there is at least one category and the user has access to them;
+ *   FALSE otherwise.
  */
 function _aggregator_has_categories() {
   return user_access('access news feeds') && (bool) db_query_range('SELECT 1 FROM {aggregator_category}', 0, 1)->fetchField();
@@ -425,7 +428,7 @@ function aggregator_block_view($delta = '') {
 }
 
 /**
- * Add/edit/delete aggregator categories.
+ * Adds/edits/deletes aggregator categories.
  *
  * @param $edit
  *   An associative array describing the category to be added/edited/deleted.
@@ -449,10 +452,12 @@ function aggregator_save_category($edit) {
         ->condition('cid', $edit['cid'])
         ->execute();
       // Make sure there is no active block for this category.
-      db_delete('block')
-        ->condition('module', 'aggregator')
-        ->condition('delta', 'category-' . $edit['cid'])
-        ->execute();
+      if (module_exists('block')) {
+        db_delete('block')
+          ->condition('module', 'aggregator')
+          ->condition('delta', 'category-' . $edit['cid'])
+          ->execute();
+      }
       $edit['title'] = '';
       $op = 'delete';
     }
@@ -511,10 +516,12 @@ function aggregator_save_feed($edit) {
       ->condition('fid', $edit['fid'])
       ->execute();
     // Make sure there is no active block for this feed.
-    db_delete('block')
-      ->condition('module', 'aggregator')
-      ->condition('delta', 'feed-' . $edit['fid'])
-      ->execute();
+    if (module_exists('block')) {
+      db_delete('block')
+        ->condition('module', 'aggregator')
+        ->condition('delta', 'feed-' . $edit['fid'])
+        ->execute();
+    }
   }
   elseif (!empty($edit['title'])) {
     $edit['fid'] = db_insert('aggregator_feed')
@@ -570,6 +577,12 @@ function aggregator_remove($feed) {
     ->execute();
 }
 
+/**
+ * Gets the fetcher, parser, and processors.
+ *
+ * @return
+ *   An array containing the fetcher, parser, and processors.
+ */
 function _aggregator_get_variables() {
   // Fetch the feed.
   $fetcher = variable_get('aggregator_fetcher', 'aggregator');
@@ -656,10 +669,11 @@ function aggregator_refresh($feed) {
 }
 
 /**
- * Load an aggregator feed.
+ * Loads an aggregator feed.
  *
  * @param $fid
  *   The feed id.
+ *
  * @return
  *   An object describing the feed.
  */
@@ -673,10 +687,11 @@ function aggregator_feed_load($fid) {
 }
 
 /**
- * Load an aggregator category.
+ * Loads an aggregator category.
  *
  * @param $cid
  *   The category id.
+ *
  * @return
  *   An associative array describing the category.
  */
@@ -705,10 +720,11 @@ function theme_aggregator_block_item($variables) {
 }
 
 /**
- * Safely render HTML content, as allowed.
+ * Safely renders HTML content, as allowed.
  *
  * @param $value
  *   The content to be filtered.
+ *
  * @return
  *   The filtered content.
  */
@@ -717,14 +733,13 @@ function aggregator_filter_xss($value) {
 }
 
 /**
- * Check and sanitize aggregator configuration.
+ * Checks and sanitizes the aggregator configuration.
  *
- * Goes through all fetchers, parsers and processors and checks whether they are
- * available.
- * If one is missing resets to standard configuration.
+ * Goes through all fetchers, parsers and processors and checks whether they
+ * are available. If one is missing resets to standard configuration.
  *
  * @return
- *   TRUE if this function reset the configuration FALSE if not.
+ *   TRUE if this function resets the configuration; FALSE if not.
  */
 function aggregator_sanitize_configuration() {
   $reset = FALSE;
@@ -755,6 +770,7 @@ function aggregator_sanitize_configuration() {
  *
  * @param $count
  *   Items count.
+ *
  * @return
  *   Plural-formatted "@count items"
  */
index 8074ae8..cd1c4cb 100644 (file)
@@ -160,11 +160,13 @@ function aggregator_feed_items_load($type, $data = NULL) {
  * @param $items
  *   The items to be listed.
  * @param $op
- *   Which form should be added to the items. Only 'categorize' is now recognized.
+ *   Which form should be added to the items. Only 'categorize' is now
+ *   recognized.
  * @param $feed_source
  *   The feed source URL.
+ *
  * @return
- *   The items HTML.
+ *   The rendered list of items for a feed.
  */
 function _aggregator_page_list($items, $op, $feed_source = '') {
   if (user_access('administer news feeds') && ($op == 'categorize')) {
@@ -184,14 +186,13 @@ function _aggregator_page_list($items, $op, $feed_source = '') {
 }
 
 /**
- * Form builder; build the page list form.
+ * Form constructor to build the page list form.
  *
  * @param $items
  *   An array of the feed items.
  * @param $feed_source
  *   The feed source URL.
- * @return
- *   The form structure.
+ *
  * @ingroup forms
  * @see aggregator_categorize_items_submit()
  */
@@ -236,7 +237,7 @@ function aggregator_categorize_items($items, $feed_source = '') {
 }
 
 /**
- * Process aggregator_categorize_items() form submissions.
+ * Form submission handler for aggregator_categorize_items().
  */
 function aggregator_categorize_items_submit($form, &$form_state) {
   if (!empty($form_state['values']['categories'])) {
@@ -293,7 +294,7 @@ function theme_aggregator_categorize_items($variables) {
 }
 
 /**
- * Process variables for aggregator-wrapper.tpl.php.
+ * Processes variables for aggregator-wrapper.tpl.php.
  *
  * @see aggregator-wrapper.tpl.php
  */
@@ -302,7 +303,7 @@ function template_preprocess_aggregator_wrapper(&$variables) {
 }
 
 /**
- * Process variables for aggregator-item.tpl.php.
+ * Processes variables for aggregator-item.tpl.php.
  *
  * @see aggregator-item.tpl.php
  */
@@ -499,7 +500,7 @@ function theme_aggregator_page_opml($variables) {
 }
 
 /**
- * Process variables for aggregator-summary-items.tpl.php.
+ * Processes variables for aggregator-summary-items.tpl.php.
  *
  * @see aggregator-summary-items.tpl.php
  */
@@ -510,7 +511,7 @@ function template_preprocess_aggregator_summary_items(&$variables) {
 }
 
 /**
- * Process variables for aggregator-summary-item.tpl.php.
+ * Processes variables for aggregator-summary-item.tpl.php.
  *
  * @see aggregator-summary-item.tpl.php
  */
@@ -530,7 +531,7 @@ function template_preprocess_aggregator_summary_item(&$variables) {
 }
 
 /**
- * Process variables for aggregator-feed-source.tpl.php.
+ * Processes variables for aggregator-feed-source.tpl.php.
  *
  * @see aggregator-feed-source.tpl.php
  */
index e9f1d2e..91fbe3a 100644 (file)
@@ -54,12 +54,13 @@ function aggregator_aggregator_parse($feed) {
 }
 
 /**
- * Parse a feed and store its items.
+ * Parses a feed and stores its items.
  *
  * @param $data
  *   The feed data.
  * @param $feed
  *   An object describing the feed to be parsed.
+ *
  * @return
  *   FALSE on error, TRUE otherwise.
  */
@@ -164,7 +165,9 @@ function aggregator_parse_feed(&$data, $feed) {
 }
 
 /**
- * Callback function used by the XML parser.
+ * Performs an action when an opening tag is encountered.
+ *
+ * Callback function used by xml_parse() within aggregator_parse_feed().
  */
 function aggregator_element_start($parser, $name, $attributes) {
   global $item, $element, $tag, $items, $channel;
@@ -212,7 +215,9 @@ function aggregator_element_start($parser, $name, $attributes) {
 }
 
 /**
- * Call-back function used by the XML parser.
+ * Performs an action when a closing tag is encountered.
+ *
+ * Callback function used by xml_parse() within aggregator_parse_feed().
  */
 function aggregator_element_end($parser, $name) {
   global $element;
@@ -234,7 +239,9 @@ function aggregator_element_end($parser, $name) {
 }
 
 /**
- * Callback function used by the XML parser.
+ * Performs an action when data is encountered.
+ *
+ * Callback function used by xml_parse() within aggregator_parse_feed().
  */
 function aggregator_element_data($parser, $data) {
   global $channel, $element, $items, $item, $image, $tag;
@@ -281,14 +288,15 @@ function aggregator_element_data($parser, $data) {
 }
 
 /**
- * Parse the W3C date/time format, a subset of ISO 8601.
+ * Parses the W3C date/time format, a subset of ISO 8601.
  *
- * PHP date parsing functions do not handle this format.
- * See http://www.w3.org/TR/NOTE-datetime for more information.
- * Originally from MagpieRSS (http://magpierss.sourceforge.net/).
+ * PHP date parsing functions do not handle this format. See
+ * http://www.w3.org/TR/NOTE-datetime for more information. Originally from
+ * MagpieRSS (http://magpierss.sourceforge.net/).
  *
  * @param $date_str
  *   A string with a potentially W3C DTF date.
+ *
  * @return
  *   A timestamp if parsed successfully or FALSE if not.
  */
index 79261b6..7fa86a9 100644 (file)
@@ -117,7 +117,7 @@ function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
     $form['modules']['aggregator']['aggregator_teaser_length'] = array(
       '#type' => 'select',
       '#title' => t('Length of trimmed description'),
-      '#default_value' => 600,
+      '#default_value' => variable_get('aggregator_teaser_length', 600),
       '#options' => drupal_map_assoc(array(0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000), '_aggregator_characters'),
       '#description' => t("The maximum number of characters used in the trimmed version of content.")
     );
@@ -126,14 +126,17 @@ function aggregator_form_aggregator_admin_form_alter(&$form, $form_state) {
 }
 
 /**
- * Helper function for teaser length choices.
+ * Creates display text for teaser length option values.
+ *
+ * Callback for drupal_map_assoc() within
+ * aggregator_form_aggregator_admin_form_alter().
  */
 function _aggregator_characters($length) {
   return ($length == 0) ? t('Unlimited') : format_plural($length, '1 character', '@count characters');
 }
 
 /**
- * Add/edit/delete an aggregator item.
+ * Adds/edits/deletes an aggregator item.
  *
  * @param $edit
  *   An associative array describing the item to be added/edited/deleted.
@@ -175,7 +178,7 @@ function aggregator_save_item($edit) {
 }
 
 /**
- * Expire feed items on $feed that are older than aggregator_clear.
+ * Expires items from a feed depending on expiration settings.
  *
  * @param $feed
  *   Object describing feed.
index fd3e852..5609d68 100644 (file)
@@ -289,6 +289,38 @@ EOF;
   }
 }
 
+/**
+ * Tests aggregator configuration settings.
+ */
+class AggregatorConfigurationTestCase extends AggregatorTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Aggregator configuration',
+      'description' => 'Test aggregator settings page.',
+      'group' => 'Aggregator',
+    );
+  }
+
+  /**
+   * Tests the settings form to ensure the correct default values are used.
+   */
+  function testSettingsPage() {
+    $edit = array(
+      'aggregator_allowed_html_tags' => '<a>',
+      'aggregator_summary_items' => 10,
+      'aggregator_clear' => 3600,
+      'aggregator_category_selector' => 'select',
+      'aggregator_teaser_length' => 200,
+