diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2313c11147a845fa4c80a69fe34e024aa9f69c68..b68b7fa92338ebded531108032445265719f422d 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,45 @@ +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. + Drupal 7.10, 2011-12-05 ---------------------- - Fixed Content-Language HTTP header to not cause issues with Drush 5.x. @@ -18,7 +59,6 @@ Drupal 7.10, 2011-12-05 - Numerous documentation improvements. - Additional automated test coverage. - Drupal 7.9, 2011-10-26 ---------------------- - Critical fixes to OpenID to spec violations that could allow for diff --git a/INSTALL.mysql.txt b/INSTALL.mysql.txt index a7c292e3171e1c1b7a6c142b00862b1a94989b8c..bee581108026d92b77646ef18684c7c0fb24138d 100644 --- a/INSTALL.mysql.txt +++ b/INSTALL.mysql.txt @@ -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.* diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index 103eb5a6adcb49554f4820f04ab5756496f94032..35eecf836e48f6c4ddd13dc0a1e8b71ef118a926 100644 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -7,7 +7,7 @@ Branch maintainers ------------------ The Drupal Core branch maintainers oversee the development of Drupal as a whole. -The branch mainainers for Drupal 7 are: +The branch maintainers for Drupal 7 are: - Dries Buytaert 'dries' <http://drupal.org/user/1> - Angela Byron 'webchick' <http://drupal.org/user/24967> @@ -133,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 @@ -257,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> diff --git a/UPGRADE.txt b/UPGRADE.txt index c6fee9874c83bddc6a3802b5d56edf22a97e5bdb..c993df7e1ecf87e731e1506822147c36e56a24d3 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -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. diff --git a/includes/actions.inc b/includes/actions.inc index 760de8300b38b838b750d21602cc37446c086e56..ed43af4fddb0fcd10da8e57091c865a9db5e8d25 100644 --- a/includes/actions.inc +++ b/includes/actions.inc @@ -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); } - diff --git a/includes/ajax.inc b/includes/ajax.inc index cda55b42466327737044cd650e78182e64e8908f..fb07477d6da6aa56395bd7f4333e3405c3a24162 100644 --- a/includes/ajax.inc +++ b/includes/ajax.inc @@ -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 @@ -101,7 +102,7 @@ * 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. * @@ -188,11 +189,11 @@ * 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: @@ -222,7 +223,7 @@ */ /** - * 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_*() @@ -301,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. @@ -361,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(); @@ -396,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'])) { @@ -414,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: @@ -547,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. @@ -570,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. @@ -589,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 @@ -1203,4 +1209,3 @@ function ajax_command_restripe($selector) { 'selector' => $selector, ); } - diff --git a/includes/archiver.inc b/includes/archiver.inc index fec053be62330c4183e7e6656e6b8ec0ffbb94ef..3ce1173906b29625660830137e836f29c0c87bdd 100644 --- a/includes/archiver.inc +++ b/includes/archiver.inc @@ -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(); } - diff --git a/includes/authorize.inc b/includes/authorize.inc index 852860413101f1183d0043a5dc73dd7c6c603b2e..da6918ca7f37400dab7dca00e0f2deb6a651b8b9 100644 --- a/includes/authorize.inc +++ b/includes/authorize.inc @@ -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. diff --git a/includes/batch.inc b/includes/batch.inc index 727c62560c94063d31ce7882dc87643875061150..061acd4c60a3dd5c71cbf2423dddcfe520e1d769 100644 --- a/includes/batch.inc +++ b/includes/batch.inc @@ -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(); } } - diff --git a/includes/batch.queue.inc b/includes/batch.queue.inc index 8464836987b703a02e3a8a3819c7a1e6afa59a9b..ed290ee70f00dce8a17523984cc07d3d1d9785c3 100644 --- a/includes/batch.queue.inc +++ b/includes/batch.queue.inc @@ -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(); diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 2e6e7d5a4c7c3a2234599b8b883afc2aeeb36f5f..d60bf2968eaa4693c3f0fd95e01a2aac13176053 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '7.10'); +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); @@ -289,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. @@ -307,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. @@ -323,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]; @@ -336,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]); } @@ -379,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) { @@ -413,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. @@ -433,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. @@ -457,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. @@ -600,7 +613,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. @@ -662,7 +675,7 @@ function drupal_override_server_variables($variables = array()) { } /** - * Initialize PHP environment. + * Initializes the PHP environment. */ function drupal_environment_initialize() { if (!isset($_SERVER['HTTP_REFERER'])) { @@ -721,7 +734,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. @@ -731,8 +744,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; @@ -824,9 +836,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 @@ -928,11 +941,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 @@ -1039,7 +1052,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 @@ -1071,7 +1084,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. @@ -1090,7 +1103,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. @@ -1112,8 +1125,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). @@ -1145,7 +1159,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). @@ -1181,11 +1195,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. @@ -1202,6 +1217,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). */ @@ -1215,9 +1232,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. @@ -1252,7 +1270,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 @@ -1295,7 +1313,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 @@ -1400,7 +1418,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'); @@ -1453,10 +1471,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 @@ -1465,8 +1483,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 @@ -1479,6 +1498,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()) { @@ -1517,7 +1537,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. @@ -1559,7 +1579,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. @@ -1598,6 +1618,7 @@ function check_plain($text) { * * @param $text * The text to check. + * * @return * TRUE if the text is valid UTF-8, FALSE if not. */ @@ -1639,7 +1660,7 @@ function request_uri() { } /** - * Log an exception. + * Logs an exception. * * This is a wrapper function for watchdog() which automatically decodes an * exception. @@ -1680,7 +1701,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 @@ -1740,7 +1761,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. @@ -1776,12 +1797,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, @@ -1809,7 +1831,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. @@ -1826,7 +1850,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 @@ -1851,7 +1877,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 @@ -1860,6 +1886,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. */ @@ -1885,7 +1912,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. @@ -1904,7 +1931,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. @@ -1951,7 +1979,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. @@ -1969,7 +1997,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. @@ -2012,7 +2040,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); } /** @@ -2073,20 +2102,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. @@ -2165,7 +2196,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; @@ -2180,7 +2211,7 @@ function drupal_get_user_timezone() { } /** - * Custom PHP error handler. + * Provides custom PHP error handling. * * @param $error_level * The level of the error raised. @@ -2191,7 +2222,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'; @@ -2199,7 +2231,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 @@ -2227,7 +2259,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. @@ -2242,7 +2274,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; @@ -2298,7 +2330,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 @@ -2350,7 +2382,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; @@ -2367,7 +2399,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'); @@ -2390,8 +2422,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 @@ -2427,7 +2458,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; @@ -2487,7 +2518,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'; @@ -2530,7 +2561,7 @@ function get_t() { } /** - * Initialize all the defined language types. + * Initializes all the defined language types. */ function drupal_language_initialize() { $types = language_types(); @@ -2555,7 +2586,7 @@ function drupal_language_initialize() { } /** - * 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 @@ -2570,7 +2601,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 @@ -2580,7 +2611,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())); @@ -2637,7 +2668,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 @@ -2707,16 +2738,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 '/' @@ -2756,6 +2787,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 @@ -2805,7 +2838,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(). @@ -2841,11 +2874,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; @@ -2856,7 +2895,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(). @@ -2926,13 +2965,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. */ @@ -2941,13 +2981,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. */ @@ -2956,7 +2997,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 @@ -2965,6 +3006,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. @@ -3036,7 +3078,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. @@ -3047,7 +3089,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 @@ -3065,7 +3107,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 @@ -3216,7 +3258,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. @@ -3226,7 +3268,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))); @@ -3234,7 +3276,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). @@ -3247,7 +3290,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". @@ -3282,7 +3325,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(); diff --git a/includes/cache-install.inc b/includes/cache-install.inc index d9bb0f92ea5be05e4ddb53931dd4e6413af7da7c..9e0dd01dee854cf4ca63b22b82a15d71779951d6 100644 --- a/includes/cache-install.inc +++ b/includes/cache-install.inc @@ -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, @@ -15,17 +15,30 @@ * 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; } diff --git a/includes/cache.inc b/includes/cache.inc index 8666874ac6250efd71ec4ab873e6f0b39ada09b0..eb7f0904057b02b46ec1ea595e95a1dbdbfcbe40 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -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); diff --git a/includes/common.inc b/includes/common.inc index 9d6bd3c0607579157997b53b8427b3a7ad7b9229..1ad97c4e950781ea2a46e0f5e1d7ae9b6e162ed1 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -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. @@ -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. @@ -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. @@ -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(). * @@ -2186,7 +2217,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. @@ -2194,6 +2225,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. */ @@ -2206,7 +2238,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'. @@ -2228,7 +2260,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 @@ -2341,7 +2373,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 @@ -2449,7 +2481,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: @@ -2558,7 +2590,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(). @@ -2585,7 +2617,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 @@ -2607,7 +2639,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 @@ -2683,10 +2715,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. @@ -2697,12 +2729,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'. @@ -2766,8 +2798,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. @@ -2899,7 +2931,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 @@ -2945,7 +2977,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]]); @@ -2969,11 +3001,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 @@ -3040,6 +3085,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(); @@ -3122,6 +3168,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')); @@ -3383,8 +3430,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. @@ -3462,9 +3509,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__); @@ -3535,13 +3580,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. */ @@ -3637,7 +3683,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.) @@ -3646,6 +3692,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. */ @@ -3667,13 +3714,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. */ @@ -3682,7 +3730,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, @@ -3813,7 +3861,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 @@ -3933,7 +3981,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 <script> - * 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. @@ -4030,6 +4078,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() */ @@ -4073,8 +4122,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() @@ -4246,7 +4297,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 @@ -4597,16 +4648,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; @@ -4621,8 +4672,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( @@ -4642,8 +4693,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 @@ -4660,14 +4711,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. * @@ -4740,8 +4791,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. @@ -4807,14 +4858,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); } /** @@ -4828,7 +4894,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. @@ -4846,7 +4912,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. @@ -4859,7 +4925,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. @@ -4873,7 +4939,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. @@ -4883,7 +4949,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. @@ -4891,6 +4957,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. @@ -4959,7 +5026,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 @@ -5011,10 +5078,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. @@ -5089,7 +5156,10 @@ 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. @@ -5208,7 +5278,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 @@ -5216,6 +5286,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. @@ -5466,13 +5537,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']; @@ -5485,8 +5556,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() @@ -5563,20 +5636,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 @@ -5593,6 +5666,7 @@ function drupal_render_page($page) { * * @param $elements * The structured array describing the data to be rendered. + * * @return * The rendered HTML. */ @@ -5608,8 +5682,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 @@ -5706,7 +5783,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. @@ -5731,7 +5808,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 @@ -5760,7 +5837,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 @@ -5786,7 +5863,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. @@ -5815,16 +5892,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)) { @@ -5845,17 +5923,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. @@ -5881,7 +5959,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 @@ -5924,9 +6002,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 @@ -5964,12 +6043,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 @@ -6008,7 +6090,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'] @@ -6055,7 +6137,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(). @@ -6081,7 +6163,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(). @@ -6121,21 +6203,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] != '#'; @@ -6151,6 +6233,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. */ @@ -6190,6 +6273,7 @@ function element_children(&$elements, $sort = FALSE) { * * @param $elements * The parent element. + * * @return * The array keys of the element's visible children. */ @@ -6384,7 +6468,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 @@ -6420,7 +6504,7 @@ 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( @@ -6631,7 +6715,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 @@ -6650,7 +6734,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 @@ -6658,6 +6742,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. @@ -6713,7 +6798,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 @@ -6744,7 +6829,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. @@ -6752,7 +6839,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']); @@ -6980,7 +7067,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: @@ -7010,6 +7097,7 @@ function drupal_parse_info_file($filename) { * * @param $data * A string to parse. + * * @return * The info array. * @@ -7073,11 +7161,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 */ @@ -7096,7 +7185,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() */ @@ -7122,7 +7211,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() */ @@ -7140,7 +7229,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. @@ -7158,6 +7247,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. @@ -7181,10 +7271,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. @@ -7192,7 +7282,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(). @@ -7217,10 +7307,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'). @@ -7274,12 +7365,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. @@ -7784,11 +7876,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. @@ -7817,14 +7910,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() @@ -7840,10 +7933,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() diff --git a/includes/database/database.inc b/includes/database/database.inc index 33000aa706ec3dab523b7903e6b05c799f4c534b..6e40b27655ca328758de4ce9adbcd51c098d20c1 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -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. diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 7d5d85998dbd53bafcf5458458c5c18bc96f1dc7..e024a7f396829a5a63e68deeeeae526ecf76725b 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -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; diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc index 39b4e9b6960c0476404c736bc4b45ea27424e938..d42a1cc3c63b454a31368c82c6418843a1212770 100644 --- a/includes/database/pgsql/database.inc +++ b/includes/database/pgsql/database.inc @@ -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()) { diff --git a/includes/database/query.inc b/includes/database/query.inc index c779687679a7a0135b78a68264e87aeff3f2e611..6020b0ea5cf12d84bc47b5e2f06f93fd341ed74d 100644 --- a/includes/database/query.inc +++ b/includes/database/query.inc @@ -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); diff --git a/includes/database/select.inc b/includes/database/select.inc index 9b587aebea4ccb7681771cc86b4c46c42b4d58c1..7e2af85e791bd24ecea8ca864cba5a281d499795 100644 --- a/includes/database/select.inc +++ b/includes/database/select.inc @@ -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])) { diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc index 3e2490b00cab86bde2d47b00919ca1e457210a31..b4e41b52774208a3510d1864760987df8e387476 100644 --- a/includes/database/sqlite/database.inc +++ b/includes/database/sqlite/database.inc @@ -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'])); + } } /** diff --git a/includes/file.inc b/includes/file.inc index 40e8349a0903ae625580be90f48b59b8e6872bca..7fd6c71c99315fcc29140776088be8e311279dbc 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -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); @@ -2266,6 +2274,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. * @@ -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; diff --git a/includes/filetransfer/filetransfer.inc b/includes/filetransfer/filetransfer.inc index 2083da9d3f802db24f97d814771a03defc0b29f4..bd2057cddca58b9bef98f094b2c7d986b6bde7a8 100644 --- a/includes/filetransfer/filetransfer.inc +++ b/includes/filetransfer/filetransfer.inc @@ -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) . '/'; } diff --git a/includes/form.inc b/includes/form.inc index e0bc9cba0b2d5c3e5879e345d4ca6d8c4ce5073a..3d5f6f22e9f29926cfae8e38e95df2eb405d3c80 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -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 ''; } diff --git a/includes/graph.inc b/includes/graph.inc index 416fad6df077f6e2b0cb14bfca1f674ba43e0b66..7fcc57a455792b51a9dd7e474aebb7d24857db60 100644 --- a/includes/graph.inc +++ b/includes/graph.inc @@ -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; } - diff --git a/includes/image.inc b/includes/image.inc index 8dc36b995bc071533818c2b98677fdf32afb7fb3..f6ae7f19b4c4ff5c77144dba7c58d49d7efe9c8b 100644 --- a/includes/image.inc +++ b/includes/image.inc @@ -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; } diff --git a/includes/json-encode.inc b/includes/json-encode.inc new file mode 100644 index 0000000000000000000000000000000000000000..1efd6ddbe3ed1f90a06e63f424d89e77ec276ad4 --- /dev/null +++ b/includes/json-encode.inc @@ -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'; + } +} diff --git a/includes/locale.inc b/includes/locale.inc index a00887d23f5a9bd1043a0e4e42b627140cd58841..0db4d4a071e602006aec756bd49dc101e08791aa 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -2293,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; } diff --git a/includes/mail.inc b/includes/mail.inc index 7272df972e2970f1faeb0aa050ae3758fff93777..13a6f4643907bd080566626041b66184b993caf2 100644 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -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'); + } } } diff --git a/includes/menu.inc b/includes/menu.inc index dad65d727fbb53fe1e06cb2a9f4e843798a349c2..25a87af120ab4e8e3d21b8d0764e3c41de60b3ad 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -274,6 +274,20 @@ define('MENU_MAX_DEPTH', 9); * @} End of "Menu tree parameters". */ +/** + * 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. * @@ -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; } /** diff --git a/includes/module.inc b/includes/module.inc index 3a019f26dc563b05041e5600baf0272433fe402b..77e35b7b03b0b7dab25b5d9ee7d2cf5ab720849e 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -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); @@ -1017,4 +1018,3 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $function($data, $context1, $context2); } } - diff --git a/includes/password.inc b/includes/password.inc index c0761da6907fc7c3723d8f5488ab8f075e699f67..d4f5f738aca25e596336b11122eb47b861662d5b 100644 --- a/includes/password.inc +++ b/includes/password.inc @@ -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); } - diff --git a/includes/registry.inc b/includes/registry.inc index 3fb14fb31b7f071325027f1591590bcf84971096..c7b8702c47ff9a44e13d6f4ae7f77fd986ed8158 100644 --- a/includes/registry.inc +++ b/includes/registry.inc @@ -183,4 +183,3 @@ function _registry_parse_file($filename, $contents, $module = '', $weight = 0) { /** * @} End of "defgroup registry". */ - diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc index e47668e3aff656749a176a75460a61ca4e4ff589..2af8c9e91287305e02d5595f604ed06fb8e1785f 100644 --- a/includes/stream_wrappers.inc +++ b/includes/stream_wrappers.inc @@ -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) { diff --git a/includes/theme.inc b/includes/theme.inc index 3868334ac2d9ab9ff91296b0634927db9470956b..da4200e56a6bb6feed660de9c741e5459f61cb00 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -372,23 +372,31 @@ class ThemeRegistry Extends DrupalCacheArray { $data = $cached->data; } else { - $complete_registry = theme_get_registry(); + // 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) { - // 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 = array_fill_keys(array_keys($complete_registry), NULL); - $this->set($this->cid, $data, $this->bin); - $this->completeRegistry = $complete_registry; - } - else { - $data = $complete_registry; + $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. @@ -420,15 +428,19 @@ class ThemeRegistry Extends DrupalCacheArray { return $this->storage[$offset]; } - public function set($cid, $data, $bin, $lock = TRUE) { - $lock_name = $cid . ':' . $bin; + public function set($data, $lock = TRUE) { + $lock_name = $this->cid . ':' . $this->bin; if (!$lock || lock_acquire($lock_name)) { - if ($cached = cache_get($cid, $this->bin)) { + 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); } - cache_set($cid, $data, $bin); + else { + $registry = $this->initializeRegistry(); + $data = array_merge($registry, $data); + } + cache_set($this->cid, $data, $this->bin); if ($lock) { lock_release($lock_name); } @@ -781,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 @@ -882,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; @@ -1515,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 @@ -2105,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. diff --git a/includes/token.inc b/includes/token.inc index edc8a962f9b0ccf91a9985a8a6b5c96c1af65984..0b05c68f473f860eeba78d719762bda52ecbc8f6 100644 --- a/includes/token.inc +++ b/includes/token.inc @@ -77,8 +77,13 @@ * 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, ''); diff --git a/includes/update.inc b/includes/update.inc index f23ff1b1f1734136582471f5643d8e83de47f8c5..048e7a5cff3387dbf86dd34dc9dd6071d3546116 100644 --- a/includes/update.inc +++ b/includes/update.inc @@ -748,7 +748,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); } diff --git a/includes/updater.inc b/includes/updater.inc index 363c6ebffaae7f8f249f314867b7144db51a900f..9801629b178ca285b1e960f3d1f1ac5df4dc8206 100644 --- a/includes/updater.inc +++ b/includes/updater.inc @@ -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); } /** diff --git a/includes/xmlrpc.inc b/includes/xmlrpc.inc index 92e5d14f0fc544b2764191957bd069f0e57804a1..b1c6f39c63de84b80f5b10d22f107e5b7b174f4d 100644 --- a/includes/xmlrpc.inc +++ b/includes/xmlrpc.inc @@ -622,4 +622,3 @@ function xmlrpc_error_msg() { function xmlrpc_clear_error() { xmlrpc_error(NULL, NULL, TRUE); } - diff --git a/includes/xmlrpcs.inc b/includes/xmlrpcs.inc index 70c7cdac3b2e81bc95a4e357248351e78c028738..118f652d233e72343ef893d056b399a03c82c032 100644 --- a/includes/xmlrpcs.inc +++ b/includes/xmlrpcs.inc @@ -382,4 +382,3 @@ function xmlrpc_server_method_help($method) { $xmlrpc_server = xmlrpc_server_get(); return $xmlrpc_server->help[$method]; } - diff --git a/misc/ajax.js b/misc/ajax.js index 830c8aa1c98e0baeeff8385fbe9e486594e2f75f..900ca1d22c5c4296c4f2fffacce650b5679e5faf 100644 --- a/misc/ajax.js +++ b/misc/ajax.js @@ -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. diff --git a/misc/autocomplete.js b/misc/autocomplete.js index 5e85be44fc5f372e1d4a6ccc4cef2454e3be7376..02a886c216f4d08d9dbda83eaed17caa07148da5 100644 --- a/misc/autocomplete.js +++ b/misc/autocomplete.js @@ -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 diff --git a/misc/drupal.js b/misc/drupal.js index 7ae737c639bc171c3a88a4202f449f5010b5a8c6..83b0884280845291c4c494d147b76f0e64ffe910 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -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. diff --git a/modules/aggregator/aggregator-feed-source.tpl.php b/modules/aggregator/aggregator-feed-source.tpl.php index 6a684bdb7faea7d8d35ffd6f96fa6137acc88080..f9cfa55561ec426c65fea173b4334bf846572bff 100644 --- a/modules/aggregator/aggregator-feed-source.tpl.php +++ b/modules/aggregator/aggregator-feed-source.tpl.php @@ -17,6 +17,8 @@ * * @see template_preprocess() * @see template_preprocess_aggregator_feed_source() + * + * @ingroup themeable */ ?> <div class="feed-source"> diff --git a/modules/aggregator/aggregator-item.tpl.php b/modules/aggregator/aggregator-item.tpl.php index e9ad1e0d75607795d782ffd1e3d8485aaa30d80f..74b2284cbe383265ba00a7a2d797e5c170618964 100644 --- a/modules/aggregator/aggregator-item.tpl.php +++ b/modules/aggregator/aggregator-item.tpl.php @@ -16,6 +16,8 @@ * * @see template_preprocess() * @see template_preprocess_aggregator_item() + * + * @ingroup themeable */ ?> <div class="feed-item"> diff --git a/modules/aggregator/aggregator-summary-item.tpl.php b/modules/aggregator/aggregator-summary-item.tpl.php index fcd57c7a4629316e027754a3f8a2dec28a778304..f9199a52ef8808b7284178ff402748db65cdf06d 100644 --- a/modules/aggregator/aggregator-summary-item.tpl.php +++ b/modules/aggregator/aggregator-summary-item.tpl.php @@ -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> diff --git a/modules/aggregator/aggregator-summary-items.tpl.php b/modules/aggregator/aggregator-summary-items.tpl.php index 0e2133a1e3b08a3419a003b71df52d2256b8806a..4a0551d7139862fb19f284418f28e931fddc9cb7 100644 --- a/modules/aggregator/aggregator-summary-items.tpl.php +++ b/modules/aggregator/aggregator-summary-items.tpl.php @@ -14,6 +14,8 @@ * * @see template_preprocess() * @see template_preprocess_aggregator_summary_items() + * + * @ingroup themeable */ ?> <h3><?php print $title; ?></h3> diff --git a/modules/aggregator/aggregator-wrapper.tpl.php b/modules/aggregator/aggregator-wrapper.tpl.php index 80b903271d4f1974403b9e06b8734be3dce907e8..2fa51a70abc43e21f4f857fe51119373f8e77d58 100644 --- a/modules/aggregator/aggregator-wrapper.tpl.php +++ b/modules/aggregator/aggregator-wrapper.tpl.php @@ -10,6 +10,8 @@ * * @see template_preprocess() * @see template_preprocess_aggregator_wrapper() + * + * @ingroup themeable */ ?> <div id="aggregator"> diff --git a/modules/aggregator/aggregator.admin.inc b/modules/aggregator/aggregator.admin.inc index 08087afb2b868d2dde47fadc0d63a3ffbcb35bbc..9f92a67052a7970fa4a975ea48406da19e93fd6b 100644 --- a/modules/aggregator/aggregator.admin.inc +++ b/modules/aggregator/aggregator.admin.inc @@ -33,7 +33,7 @@ function aggregator_view() { ($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never')), l(t('edit'), "admin/config/services/aggregator/edit/feed/$feed->fid"), l(t('remove items'), "admin/config/services/aggregator/remove/$feed->fid"), - l(t('update items'), "admin/config/services/aggregator/update/$feed->fid"), + l(t('update items'), "admin/config/services/aggregator/update/$feed->fid", array('query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")))), ); } $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/config/services/aggregator/add/feed'))))); @@ -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(); @@ -386,13 +404,17 @@ function _aggregator_parse_opml($opml) { * An object describing the feed to be refreshed. */ function aggregator_admin_refresh_feed($feed) { + if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'aggregator/update/' . $feed->fid)) { + return MENU_ACCESS_DENIED; + } aggregator_refresh($feed); drupal_goto('admin/config/services/aggregator'); } /** - * 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) { @@ -500,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']); @@ -508,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() @@ -536,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')) { @@ -554,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) { diff --git a/modules/aggregator/aggregator.api.php b/modules/aggregator/aggregator.api.php index ea119276023adbbb7fb70d74a3bc0c3eefbde533..0f708eb85ef3423825763aafcd1c4f6622b4063d 100644 --- a/modules/aggregator/aggregator.api.php +++ b/modules/aggregator/aggregator.api.php @@ -11,21 +11,21 @@ */ /** - * 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,19 +67,19 @@ 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 the active parser on * admin/config/services/aggregator. Only one parser can be active at a time. * * @param $feed - * An object describing the resource to be parsed: $feed->source_string + * 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. @@ -118,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 @@ -145,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() @@ -176,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. @@ -203,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". diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info index 3b0837299f936b53225c74b9b6043fbd58f34b8f..087a526b09a7649bcda223cdc6ac42c6bc0eb410 100644 --- a/modules/aggregator/aggregator.info +++ b/modules/aggregator/aggregator.info @@ -7,8 +7,8 @@ files[] = aggregator.test configure = admin/config/services/aggregator/settings stylesheets[all][] = aggregator.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module index eafb61ed3083ce68d78573c74b83b172fca48fa0..9319ad9b82bec44f93627715dc4fb0dfaf802077 100644 --- a/modules/aggregator/aggregator.module +++ b/modules/aggregator/aggregator.module @@ -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" */ diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc index 8074ae82e8ee348c86abf7a6017923834603c264..cd1c4cb2c08cc0600bc43c248b36aed654c538d5 100644 --- a/modules/aggregator/aggregator.pages.inc +++ b/modules/aggregator/aggregator.pages.inc @@ -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 */ diff --git a/modules/aggregator/aggregator.parser.inc b/modules/aggregator/aggregator.parser.inc index e9f1d2e8a86500e7bf93a666a48ee6f74bf4ba43..91fbe3aa15b4e5e6a7f6d713b822d1c507207856 100644 --- a/modules/aggregator/aggregator.parser.inc +++ b/modules/aggregator/aggregator.parser.inc @@ -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. */ diff --git a/modules/aggregator/aggregator.processor.inc b/modules/aggregator/aggregator.processor.inc index 79261b6186b3ea09153f53d3868aee7e31f4ca64..7fa86a9ad83ee239b9dabfb32822cc0d1c102ceb 100644 --- a/modules/aggregator/aggregator.processor.inc +++ b/modules/aggregator/aggregator.processor.inc @@ -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. diff --git a/modules/aggregator/aggregator.test b/modules/aggregator/aggregator.test index 0d1e31ba545b1097335370c2439b9dd120a1e5eb..5609d68aed22a849dea3095b056275a3c1975a17 100644 --- a/modules/aggregator/aggregator.test +++ b/modules/aggregator/aggregator.test @@ -92,8 +92,13 @@ class AggregatorTestCase extends DrupalWebTestCase { $this->drupalGet($feed->url); $this->assertResponse(200, t('!url is reachable.', array('!url' => $feed->url))); - // Refresh the feed (simulated link click). + // Attempt to access the update link directly without an access token. $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid); + $this->assertResponse(403); + + // Refresh the feed (simulated link click). + $this->drupalGet('admin/config/services/aggregator'); + $this->clickLink('update items'); // Ensure we have the right number of items. $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid)); @@ -284,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, + ); + $this->drupalPost('admin/config/services/aggregator/settings', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + + foreach ($edit as $name => $value) { + $this->assertFieldByName($name, $value, t('"@name" has correct default value.', array('@name' => $name))); + } + } +} + class AddFeedTestCase extends AggregatorTestCase { public static function getInfo() { return array( @@ -466,8 +503,8 @@ class UpdateFeedItemTestCase extends AggregatorTestCase { $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), t('The feed !name has been added.', array('!name' => $edit['title']))); $feed = db_query("SELECT * FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetchObject(); - $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid); + aggregator_refresh($feed); $before = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(); // Sleep for 3 second. @@ -481,10 +518,9 @@ class UpdateFeedItemTestCase extends AggregatorTestCase { 'modified' => 0, )) ->execute(); - $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid); + aggregator_refresh($feed); $after = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(); - $this->assertTrue($before === $after, t('Publish timestamp of feed item was not updated (!before === !after)', array('!before' => $before, '!after' => $after))); } } @@ -884,4 +920,3 @@ class FeedParserTestCase extends AggregatorTestCase { $this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', array(':link' => 'http://example.org/2003/12/13/atom03'))->fetchField(), 'Atom entry id element is parsed correctly.'); } } - diff --git a/modules/aggregator/tests/aggregator_test.info b/modules/aggregator/tests/aggregator_test.info index bc4ec71d504a3852e7cc0d271da6ef60dcfc2cc5..a7fc553282f9286abfc5079820065ee11b6238e0 100644 --- a/modules/aggregator/tests/aggregator_test.info +++ b/modules/aggregator/tests/aggregator_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/block/block-admin-display-form.tpl.php b/modules/block/block-admin-display-form.tpl.php index 94b610416b22d22095556a4d8104d86de8e4e467..17a15e40d925b14bd409f8ac29f0f6aff7267910 100644 --- a/modules/block/block-admin-display-form.tpl.php +++ b/modules/block/block-admin-display-form.tpl.php @@ -21,6 +21,8 @@ * * @see template_preprocess_block_admin_display_form() * @see theme_block_admin_display() + * + * @ingroup themeable */ ?> <?php diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index f86fe96b90475c2f3b83e733cc1dd015c2a5028e..bd61790638f0cf340b8be358353bd30bde7c02bc 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -57,7 +57,7 @@ function block_admin_display_prepare_blocks($theme) { } /** - * Form builder for the main blocks administration form. + * Form constructor for the main block administration form. * * @param $blocks * An array of blocks, as returned by block_admin_display_prepare_blocks(). @@ -165,7 +165,7 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_r } /** - * Form submission handler for the main blocks administration form. + * Form submission handler for block_admin_display_form(). * * @see block_admin_display_form() */ @@ -197,10 +197,9 @@ function block_admin_display_form_submit($form, &$form_state) { } /** - * Helper function for sorting blocks on admin/structure/block. + * Sorts active blocks by region, then by weight; sorts inactive blocks by name. * - * Active blocks are sorted by region, then by weight. - * Disabled blocks are sorted by name. + * Callback for usort() in block_admin_display_prepare_blocks(). */ function _block_compare($a, $b) { global $theme_key; @@ -240,7 +239,7 @@ function _block_compare($a, $b) { } /** - * Form builder for the block configuration form. + * Form constructor for the block configuration form. * * Also used by block_add_block_form() for adding a new custom block. * @@ -441,7 +440,7 @@ function block_admin_configure($form, &$form_state, $module, $delta) { } /** - * Form validation handler for the block configuration form. + * Form validation handler for block_admin_configure(). * * @see block_admin_configure() * @see block_admin_configure_submit() @@ -459,7 +458,7 @@ function block_admin_configure_validate($form, &$form_state) { } /** - * Form submission handler for the block configuration form. + * Form submission handler for block_admin_configure(). * * @see block_admin_configure() * @see block_admin_configure_validate() @@ -519,7 +518,7 @@ function block_admin_configure_submit($form, &$form_state) { } /** - * Form builder for the add block form. + * Form constructor for the add block form. * * @see block_add_block_form_validate() * @see block_add_block_form_submit() @@ -530,7 +529,7 @@ function block_add_block_form($form, &$form_state) { } /** - * Form validation handler for the add block form. + * Form validation handler for block_add_block_form(). * * @see block_add_block_form() * @see block_add_block_form_submit() @@ -544,7 +543,7 @@ function block_add_block_form_validate($form, &$form_state) { } /** - * Form submission handler for the add block form. + * Form submission handler for block_add_block_form(). * * Saves the new custom block. * @@ -609,7 +608,7 @@ function block_add_block_form_submit($form, &$form_state) { } /** - * Form builder for the custom block deletion form. + * Form constructor for the custom block deletion form. * * @param $module * The name of the module that implements the block to be deleted. This should @@ -629,7 +628,7 @@ function block_custom_block_delete($form, &$form_state, $module, $delta) { } /** - * Form submission handler for the custom block deletion form. + * Form submission handler for block_custom_block_delete(). * * @see block_custom_block_delete() */ diff --git a/modules/block/block.info b/modules/block/block.info index a15bc9a6a0855ca8bf0593c9293e015fb1427f8a..2ad2a71a639c30c9613ec3876bf85e4c4fa576e7 100644 --- a/modules/block/block.info +++ b/modules/block/block.info @@ -6,8 +6,8 @@ core = 7.x files[] = block.test configure = admin/structure/block -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/block/block.module b/modules/block/block.module index 754febd271d84b13bb94e15b11465efd6a8c6c9d..24319089e35b3eb6438fa760b5beb4549bea3bbf 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -16,27 +16,27 @@ define('BLOCK_REGION_NONE', -1); define('BLOCK_CUSTOM_FIXED', 0); /** - * Show this block by default, but let individual users hide it. + * Shows this block by default, but lets individual users hide it. */ define('BLOCK_CUSTOM_ENABLED', 1); /** - * Hide this block by default but let individual users show it. + * Hides this block by default but lets individual users show it. */ define('BLOCK_CUSTOM_DISABLED', 2); /** - * Show this block on every page except the listed pages. + * Shows this block on every page except the listed pages. */ define('BLOCK_VISIBILITY_NOTLISTED', 0); /** - * Show this block on only the listed pages. + * Shows this block on only the listed pages. */ define('BLOCK_VISIBILITY_LISTED', 1); /** - * Show this block if the associated PHP code returns TRUE. + * Shows this block if the associated PHP code returns TRUE. */ define('BLOCK_VISIBILITY_PHP', 2); @@ -249,7 +249,7 @@ function block_block_view($delta = '') { /** * Implements hook_page_build(). * - * Render blocks into their regions. + * Renders blocks into their regions. */ function block_page_build(&$page) { global $theme; @@ -305,10 +305,13 @@ function block_page_build(&$page) { } /** - * Get a renderable array of a region containing all enabled blocks. + * Gets a renderable array of a region containing all enabled blocks. * * @param $region * The requested region. + * + * @return + * A renderable array of a region containing all enabled blocks. */ function block_get_blocks_by_region($region) { $build = array(); @@ -319,10 +322,11 @@ function block_get_blocks_by_region($region) { } /** - * Get an array of blocks suitable for drupal_render(). + * Gets an array of blocks suitable for drupal_render(). * * @param $list * A list of blocks such as that returned by block_list(). + * * @return * A renderable array. */ @@ -353,7 +357,7 @@ function _block_get_renderable_array($list = array()) { } /** - * Update the 'block' DB table with the blocks currently exported by modules. + * Updates the 'block' DB table with the blocks currently exported by modules. * * @param $theme * The theme to rehash blocks for. If not provided, defaults to the currently @@ -473,6 +477,7 @@ function _block_rehash($theme = NULL) { * * @param $bid * ID of the block to get information for. + * * @return * Associative array of information stored in the database for this block. * Array keys: @@ -486,7 +491,14 @@ function block_custom_block_get($bid) { } /** - * Define the custom block form. + * Form constructor for the custom block form. + * + * @param $edit + * (optional) An associative array of information retrieved by + * block_custom_get_block() if an existing block is being edited, or an empty + * array otherwise. Defaults to array(). + * + * @ingroup forms */ function block_custom_block_form($edit = array()) { $edit += array( @@ -528,6 +540,7 @@ function block_custom_block_form($edit = array()) { * - format: Filter ID of the filter format for the body. * @param $delta * Block ID of the block to save. + * * @return * Always returns TRUE. */ @@ -544,7 +557,7 @@ function block_custom_block_save($edit, $delta) { } /** - * Implements hook_form_FORM_ID_alter(). + * Implements hook_form_FORM_ID_alter() for user_profile_form(). */ function block_form_user_profile_form_alter(&$form, &$form_state) { if ($form['#user_category'] == 'account') { @@ -588,7 +601,10 @@ function block_user_presave(&$edit, $account, $category) { } /** - * Initialize blocks for enabled themes. + * Initializes blocks for enabled themes. + * + * @param $theme_list + * An array of theme names. */ function block_themes_enabled($theme_list) { foreach ($theme_list as $theme) { @@ -597,7 +613,7 @@ function block_themes_enabled($theme_list) { } /** - * Assign an initial, default set of blocks for a theme. + * Assigns an initial, default set of blocks for a theme. * * This function is called the first time a new theme is enabled. The new theme * gets a copy of the default theme's blocks, with the difference that if a @@ -628,7 +644,7 @@ function block_theme_initialize($theme) { } /** - * Return all blocks in the specified region for the current user. + * Returns all blocks in the specified region for the current user. * * @param $region * The name of a region. @@ -640,7 +656,7 @@ function block_theme_initialize($theme) { * empty to see how many columns are going to be displayed. * * @todo - * Now that the blocks table has a primary key, we should use that as the + * Now that the block table has a primary key, we should use that as the * array key instead of MODULE_DELTA. */ function block_list($region) { @@ -662,7 +678,7 @@ function block_list($region) { } /** - * Load a block object from the database. + * Loads a block object from the database. * * @param $module * Name of the module that implements the block to load. @@ -690,7 +706,10 @@ function block_load($module, $delta) { } /** - * Load blocks information from the database. + * Loads blocks' information from the database. + * + * @return + * An array of blocks grouped by region. */ function _block_load_blocks() { global $theme_key; @@ -721,8 +740,8 @@ function _block_load_blocks() { /** * Implements hook_block_list_alter(). * - * Check the page, user role and user specific visibilty settings. - * Remove the block if the visibility conditions are not met. + * Checks the page, user role, and user-specific visibilty settings. + * Removes the block if the visibility conditions are not met. */ function block_block_list_alter(&$blocks) { global $user, $theme_key; @@ -916,9 +935,9 @@ function block_flush_caches() { } /** - * Process variables for block.tpl.php + * Processes variables for block.tpl.php. * - * Prepare the values passed to the theme_block function to be passed + * Prepares the values passed to the theme_block function to be passed * into a pluggable template engine. Uses block properties to generate a * series of template file suggestions. If none are found, the default * block.tpl.php is used. @@ -967,7 +986,7 @@ function template_preprocess_block(&$variables) { /** * Implements hook_user_role_delete(). * - * Remove deleted role from blocks that use it. + * Removes deleted role from blocks that use it. */ function block_user_role_delete($role) { db_delete('block_role') @@ -1019,7 +1038,7 @@ function block_admin_paths() { /** * Implements hook_modules_uninstalled(). * - * Cleanup {block} and {block_role} tables from modules' blocks. + * Cleans up {block} and {block_role} tables from modules' blocks. */ function block_modules_uninstalled($modules) { db_delete('block') diff --git a/modules/block/block.test b/modules/block/block.test index 9639b2c106962ccf6887541958f487809a5ae595..323034062d3f3659dc007df1342ca58db30bac41 100644 --- a/modules/block/block.test +++ b/modules/block/block.test @@ -764,6 +764,10 @@ class BlockHiddenRegionTestCase extends DrupalWebTestCase { ); } + function setUp() { + parent::setUp(array('block_test')); + } + /** * Tests that hidden regions do not inherit blocks when a theme is enabled. */ diff --git a/modules/block/block.tpl.php b/modules/block/block.tpl.php index b1946374a22ca716f95a6b2f51a52b538f9b3455..f0bfa5cab44e8afa4db91f959a1e31b6a146e99e 100644 --- a/modules/block/block.tpl.php +++ b/modules/block/block.tpl.php @@ -12,11 +12,12 @@ * - $block->region: The block region embedding the current block. * - $classes: String of classes that can be used to style contextually through * CSS. It can be manipulated through the variable $classes_array from - * preprocess functions. The default values can be one or more of the following: + * preprocess functions. The default values can be one or more of the + * following: * - block: The current template type, i.e., "theming hook". - * - block-[module]: The module generating the block. For example, the user module - * is responsible for handling the default user navigation block. In that case - * the class would be "block-user". + * - block-[module]: The module generating the block. For example, the user + * module is responsible for handling the default user navigation block. In + * that case the class would be 'block-user'. * - $title_prefix (array): An array containing additional output populated by * modules, intended to be displayed in front of the main title tag that * appears in the template. @@ -39,6 +40,8 @@ * @see template_preprocess() * @see template_preprocess_block() * @see template_process() + * + * @ingroup themeable */ ?> <div id="<?php print $block_html_id; ?>" class="<?php print $classes; ?>"<?php print $attributes; ?>> diff --git a/modules/block/tests/block_test.info b/modules/block/tests/block_test.info index 69aeec27d7c93f134c7e61d350a8e411ad073dab..084bd835e95713abcd51c06cf76f8908042aa41a 100644 --- a/modules/block/tests/block_test.info +++ b/modules/block/tests/block_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/block/tests/block_test.module b/modules/block/tests/block_test.module index 2abc433c98240b6c409ed10a3e27b920e8c154b0..5e06d5cf51f2216b0649c0893721f9cb8429179f 100644 --- a/modules/block/tests/block_test.module +++ b/modules/block/tests/block_test.module @@ -5,6 +5,14 @@ * Provide test blocks. */ +/** + * Implements hook_system_theme_info(). + */ +function block_test_system_theme_info() { + $themes['block_test_theme'] = drupal_get_path('module', 'block_test') . '/themes/block_test_theme/block_test_theme.info'; + return $themes; +} + /** * Implements hook_block_info(). */ diff --git a/themes/tests/block_test_theme/block_test_theme.info b/modules/block/tests/themes/block_test_theme/block_test_theme.info similarity index 79% rename from themes/tests/block_test_theme/block_test_theme.info rename to modules/block/tests/themes/block_test_theme/block_test_theme.info index 65d2580e65917d227d7c2b67bbb82605c2467c49..e05bf02fbe33fe3e7d50c103a9acf7ae86192db0 100644 --- a/themes/tests/block_test_theme/block_test_theme.info +++ b/modules/block/tests/themes/block_test_theme/block_test_theme.info @@ -13,8 +13,8 @@ regions[footer] = Footer regions[highlighted] = Highlighted regions[help] = Help -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/tests/block_test_theme/page.tpl.php b/modules/block/tests/themes/block_test_theme/page.tpl.php similarity index 100% rename from themes/tests/block_test_theme/page.tpl.php rename to modules/block/tests/themes/block_test_theme/page.tpl.php diff --git a/modules/blog/blog.info b/modules/blog/blog.info index 91ef6b1cb972292514bdadc7ccf12bf00b239879..7e3a98f6f29621ea2b09d765145e65417a6bba7c 100644 --- a/modules/blog/blog.info +++ b/modules/blog/blog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = blog.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/book/book.info b/modules/book/book.info index 653d6fc286bbefa6e5e38abc4b37484bb675e4a6..a68563a569227d507b2f0ab7d538dd7486b57686 100644 --- a/modules/book/book.info +++ b/modules/book/book.info @@ -7,8 +7,8 @@ files[] = book.test configure = admin/content/book/settings stylesheets[all][] = book.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/color/color-rtl.css b/modules/color/color-rtl.css index bfbcd499ff8f12b774b87053fa55f70758462ba5..07b754ea5d34f8da1475dafbd8f702ccafc1c003 100644 --- a/modules/color/color-rtl.css +++ b/modules/color/color-rtl.css @@ -1,3 +1,7 @@ +/** + * @file + * Right-to-left specific stylesheet for the Color module. + */ #placeholder { left: 0; diff --git a/modules/color/color.css b/modules/color/color.css index e513dadf54083053d78fd6937a4cc644ece2592f..981cff85d7eb26f9e6184174f09a76a36bae15f6 100644 --- a/modules/color/color.css +++ b/modules/color/color.css @@ -1,3 +1,7 @@ +/** + * @file + * Stylesheet for the administration pages of the Color module. + */ /* Farbtastic placement */ .color-form { diff --git a/modules/color/color.info b/modules/color/color.info index 203467fcafd093984790c7915fd2f9d82e438d3d..df5b93c97ca7d9c93060690253664a414bc6bc57 100644 --- a/modules/color/color.info +++ b/modules/color/color.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = color.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/color/color.js b/modules/color/color.js index 43099adcabf8304fb33c337633483fe0a8c489f4..d7e066454561aca40cd972ed4b0d631878a4752e 100644 --- a/modules/color/color.js +++ b/modules/color/color.js @@ -1,3 +1,8 @@ +/** + * @file + * Attaches the behaviors for the Color module. + */ + (function ($) { Drupal.behaviors.color = { @@ -65,20 +70,20 @@ Drupal.behaviors.color = { }); /** - * Render the preview. + * Renders the preview. */ function preview() { Drupal.color.callback(context, settings, form, farb, height, width); } /** - * Shift a given color, using a reference pair (ref in HSL). + * Shifts a given color, using a reference pair (ref in HSL). * * This algorithm ensures relative ordering on the saturation and luminance * axes is preserved, and performs a simple hue shift. * - * It is also symmetrical. If: shift_color(c, a, b) == d, - * then shift_color(d, b, a) == c. + * It is also symmetrical. If: shift_color(c, a, b) == d, then + * shift_color(d, b, a) == c. */ function shift_color(given, ref1, ref2) { // Convert to HSL. @@ -159,7 +164,7 @@ Drupal.behaviors.color = { } /** - * Reset the color scheme selector. + * Resets the color scheme selector. */ function resetScheme() { $('#edit-scheme', form).each(function () { @@ -167,7 +172,9 @@ Drupal.behaviors.color = { }); } - // Focus the Farbtastic on a particular field. + /** + * Focuses Farbtastic on a particular field. + */ function focus() { var input = this; // Remove old bindings. diff --git a/modules/color/color.module b/modules/color/color.module index be25c2d751fc3650547f071fc1e259eee0c56e1f..c58d6ecce01947cc8d2c42bc4e1847c9dd888789 100644 --- a/modules/color/color.module +++ b/modules/color/color.module @@ -1,4 +1,8 @@ <?php +/** + * @file + * Allows users to change the color scheme of themes. + */ /** * Implements hook_help(). @@ -70,7 +74,13 @@ function _color_theme_select_form_alter(&$form, &$form_state) { } /** - * Callback for the theme to alter the resources used. + * Replaces style sheets with color-altered style sheets. + * + * A theme that supports the color module should call this function from its + * THEME_process_html() function, so that the correct style sheets are + * included when html.tpl.php is rendered. + * + * @see theme() */ function _color_html_alter(&$vars) { global $theme_key; @@ -86,7 +96,7 @@ function _color_html_alter(&$vars) { foreach ($color_paths as $color_path) { // Color module currently requires unique file names to be used, // which allows us to compare different file paths. - if (basename($old_path) == basename($color_path)) { + if (drupal_basename($old_path) == drupal_basename($color_path)) { // Replace the path to the new css file. // This keeps the order of the stylesheets intact. $vars['css'][$old_path]['data'] = $color_path; @@ -99,7 +109,13 @@ function _color_html_alter(&$vars) { } /** - * Callback for the theme to alter the resources used. + * Replaces the logo with a color-altered logo. + * + * A theme that supports the color module should call this function from its + * THEME_process_page() function, so that the correct logo is included when + * page.tpl.php is rendered. + * + * @see theme() */ function _color_page_alter(&$vars) { global $theme_key; @@ -112,7 +128,7 @@ function _color_page_alter(&$vars) { } /** - * Retrieve the color.module info for a particular theme. + * Retrieves the Color module information for a particular theme. */ function color_get_info($theme) { static $theme_info = array(); @@ -131,7 +147,7 @@ function color_get_info($theme) { } /** - * Helper function to retrieve the color palette for a particular theme. + * Retrieves the color palette for a particular theme. */ function color_get_palette($theme, $default = FALSE) { // Fetch and expand default palette. @@ -143,7 +159,14 @@ function color_get_palette($theme, $default = FALSE) { } /** - * Form callback. Returns the configuration form. + * Form constructor for the color configuration form for a particular theme. + * + * @param $theme + * The machine name of the theme whose color settings are being configured. + * + * @see color_scheme_form_validate() + * @see color_scheme_form_submit() + * @ingroup forms */ function color_scheme_form($complete_form, &$form_state, $theme) { $base = drupal_get_path('module', 'color'); @@ -244,11 +267,11 @@ function theme_color_scheme_form($variables) { $info = $form['info']['#value']; $path = drupal_get_path('theme', $theme) . '/'; drupal_add_css($path . $info['preview_css']); - + $preview_js_path = isset($info['preview_js']) ? $path . $info['preview_js'] : drupal_get_path('module', 'color') . '/' . 'preview.js'; // Add the JS at a weight below color.js. drupal_add_js($preview_js_path, array('weight' => -1)); - + $output = ''; $output .= '<div class="color-form clearfix">'; // Color schemes @@ -272,7 +295,9 @@ function theme_color_scheme_form($variables) { } /** - * Validation handler for color change form. + * Form validation handler for color_scheme_form(). + * + * @see color_scheme_form_submit() */ function color_scheme_form_validate($form, &$form_state) { // Only accept hexadecimal CSS color strings to avoid XSS upon use. @@ -284,7 +309,9 @@ function color_scheme_form_validate($form, &$form_state) { } /** - * Submit handler for color change form. + * Form submission handler for color_scheme_form(). + * + * @see color_scheme_form_validate() */ function color_scheme_form_submit($form, &$form_state) { // Get theme coloring info. @@ -362,7 +389,7 @@ function color_scheme_form_submit($form, &$form_state) { // Copy over neutral images. foreach ($info['copy'] as $file) { - $base = basename($file); + $base = drupal_basename($file); $source = $paths['source'] . $file; $filepath = file_unmanaged_copy($source, $paths['target'] . $base); $paths['map'][$file] = $base; @@ -404,7 +431,7 @@ function color_scheme_form_submit($form, &$form_state) { // Rewrite stylesheet with new colors. $style = _color_rewrite_stylesheet($theme, $info, $paths, $palette, $style); - $base_file = basename($file); + $base_file = drupal_basename($file); $css[] = $paths['target'] . $base_file; _color_save_stylesheet($paths['target'] . $base_file, $style, $paths); } @@ -416,7 +443,7 @@ function color_scheme_form_submit($form, &$form_state) { } /** - * Rewrite the stylesheet to match the colors in the palette. + * Rewrites the stylesheet to match the colors in the palette. */ function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) { $themes = list_themes(); @@ -487,7 +514,7 @@ function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) { } /** - * Save the rewritten stylesheet to disk. + * Saves the rewritten stylesheet to disk. */ function _color_save_stylesheet($file, $style, &$paths) { $filepath = file_unmanaged_save_data($style, $file, FILE_EXISTS_REPLACE); @@ -498,7 +525,7 @@ function _color_save_stylesheet($file, $style, &$paths) { } /** - * Render images that match a given palette. + * Renders images that match a given palette. */ function _color_render_images($theme, &$info, &$paths, $palette) { // Prepare template image. @@ -544,7 +571,7 @@ function _color_render_images($theme, &$info, &$paths, $palette) { // Cut out slices. foreach ($info['slices'] as $file => $coord) { list($x, $y, $width, $height) = $coord; - $base = basename($file); + $base = drupal_basename($file); $image = drupal_realpath($paths['target'] . $base); // Cut out slice. @@ -575,16 +602,16 @@ function _color_render_images($theme, &$info, &$paths, $palette) { } /** - * Shift a given color, using a reference pair and a target blend color. + * Shifts a given color, using a reference pair and a target blend color. * * Note: this function is significantly different from the JS version, as it * is written to match the blended images perfectly. * - * Constraint: if (ref2 == target + (ref1 - target) * delta) for some fraction delta - * then (return == target + (given - target) * delta) + * Constraint: if (ref2 == target + (ref1 - target) * delta) for some fraction + * delta then (return == target + (given - target) * delta). * * Loose constraint: Preserve relative positions in saturation and luminance - * space. + * space. */ function _color_shift($given, $ref1, $ref2, $target) { // We assume that ref2 is a blend of ref1 and target and find @@ -634,7 +661,7 @@ function _color_shift($given, $ref1, $ref2, $target) { } /** - * Convert a hex triplet into a GD color. + * Converts a hex triplet into a GD color. */ function _color_gd($img, $hex) { $c = array_merge(array($img), _color_unpack($hex)); @@ -642,7 +669,7 @@ function _color_gd($img, $hex) { } /** - * Blend two hex colors and return the GD color. + * Blends two hex colors and returns the GD color. */ function _color_blend($img, $hex1, $hex2, $alpha) { $in1 = _color_unpack($hex1); @@ -656,7 +683,7 @@ function _color_blend($img, $hex1, $hex2, $alpha) { } /** - * Convert a hex color into an RGB triplet. + * Converts a hex color into an RGB triplet. */ function _color_unpack($hex, $normalize = FALSE) { if (strlen($hex) == 4) { @@ -671,7 +698,7 @@ function _color_unpack($hex, $normalize = FALSE) { } /** - * Convert an RGB triplet to a hex color. + * Converts an RGB triplet to a hex color. */ function _color_pack($rgb, $normalize = FALSE) { $out = 0; @@ -683,7 +710,7 @@ function _color_pack($rgb, $normalize = FALSE) { } /** - * Convert a HSL triplet into RGB. + * Converts an HSL triplet into RGB. */ function _color_hsl2rgb($hsl) { $h = $hsl[0]; @@ -712,7 +739,7 @@ function _color_hue2rgb($m1, $m2, $h) { } /** - * Convert an RGB triplet to HSL. + * Converts an RGB triplet to HSL. */ function _color_rgb2hsl($rgb) { $r = $rgb[0]; diff --git a/modules/color/color.test b/modules/color/color.test index 0a8e78f4810474341da40c10308690be5c714a2d..09043250b05e0a1465c02f9b8d33e9480f9a7f94 100644 --- a/modules/color/color.test +++ b/modules/color/color.test @@ -6,7 +6,7 @@ */ /** - * Test color functionality. + * Tests the Color module functionality. */ class ColorTestCase extends DrupalWebTestCase { protected $big_user; @@ -57,7 +57,7 @@ class ColorTestCase extends DrupalWebTestCase { } /** - * Test color module functionality. + * Tests the Color module functionality. */ function testColor() { foreach ($this->themes as $theme => $test_values) { @@ -66,7 +66,7 @@ class ColorTestCase extends DrupalWebTestCase { } /** - * Tests color module functionality using the given theme. + * Tests the Color module functionality using the given theme. */ function _testColor($theme, $test_values) { variable_set('theme_default', $theme); @@ -109,7 +109,7 @@ class ColorTestCase extends DrupalWebTestCase { } /** - * Test to see if the provided color is valid + * Tests whether the provided color is valid. */ function testValidColor() { variable_set('theme_default', 'bartik'); diff --git a/modules/color/preview.js b/modules/color/preview.js index 88ae95fb62c639a64180c04bdeb9b7ad0ec3b37f..67eef0b01f0fed88778bb276216f544660c031b8 100644 --- a/modules/color/preview.js +++ b/modules/color/preview.js @@ -1,3 +1,7 @@ +/** + * @file + * Attaches preview-related behavior for the Color module. + */ (function ($) { Drupal.color = { diff --git a/modules/comment/comment.info b/modules/comment/comment.info index d5b626642c5871a3eac274b7d3a7b2d5e2337cca..d150d000fa3efd9708140f86c648939552a0dc60 100644 --- a/modules/comment/comment.info +++ b/modules/comment/comment.info @@ -9,8 +9,8 @@ files[] = comment.test configure = admin/content/comment stylesheets[all][] = comment.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/comment/comment.install b/modules/comment/comment.install index 121ec22a1c7d2225678a665a6670a462e029cf8a..7312e2a3166c07dd6a45e568d212a6e6ddee5c87 100644 --- a/modules/comment/comment.install +++ b/modules/comment/comment.install @@ -165,9 +165,11 @@ function comment_update_7001() { function comment_update_7002() { db_rename_table('comments', 'comment'); - // Add user-related indexes. - db_add_index('comment', 'comment_uid', array('uid')); - db_add_index('node_comment_statistics', 'last_comment_uid', array('last_comment_uid')); + // Add user-related indexes. These may already exist from Drupal 6. + if (!db_index_exists('comment', 'comment_uid')) { + db_add_index('comment', 'comment_uid', array('uid')); + db_add_index('node_comment_statistics', 'last_comment_uid', array('last_comment_uid')); + } // Create a language column. db_add_field('comment', 'language', array( @@ -224,8 +226,11 @@ function comment_update_7004() { )); db_add_index('node_comment_statistics', 'cid', array('cid')); - // Add an index on the comment_count. - db_add_index('node_comment_statistics', 'comment_count', array('comment_count')); + // The comment_count index may have been added in Drupal 6. + if (!db_index_exists('node_comment_statistics', 'comment_count')) { + // Add an index on the comment_count. + db_add_index('node_comment_statistics', 'comment_count', array('comment_count')); + } } /** diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 239cd5bec704471f6b3a8035fbec1e30896da9c3..2793ea3b5bdb7613ae8e40639b1e2134c934cbd3 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -1232,8 +1232,8 @@ function comment_form_node_form_alter(&$form, $form_state) { // If the node doesn't have any comments, the "hidden" option makes no // sense, so don't even bother presenting it to the user. if (empty($comment_count)) { - unset($form['comment_settings']['comment']['#options'][COMMENT_NODE_HIDDEN]); - unset($form['comment_settings']['comment'][COMMENT_NODE_HIDDEN]); + $form['comment_settings']['comment'][COMMENT_NODE_HIDDEN]['#access'] = FALSE; + // Also adjust the description of the "closed" option. $form['comment_settings']['comment'][COMMENT_NODE_CLOSED]['#description'] = t('Users cannot post comments.'); } } @@ -2294,24 +2294,26 @@ function template_preprocess_comment(&$variables) { else { $variables['status'] = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'comment-unpublished' : 'comment-published'; } + // Gather comment classes. - if ($comment->uid == 0) { + // 'comment-published' class is not needed, it is either 'comment-preview' or + // 'comment-unpublished'. + if ($variables['status'] != 'comment-published') { + $variables['classes_array'][] = $variables['status']; + } + if ($variables['new']) { + $variables['classes_array'][] = 'comment-new'; + } + if (!$comment->uid) { $variables['classes_array'][] = 'comment-by-anonymous'; } else { - // Published class is not needed. It is either 'comment-preview' or 'comment-unpublished'. - if ($variables['status'] != 'comment-published') { - $variables['classes_array'][] = $variables['status']; - } - if ($comment->uid === $variables['node']->uid) { + if ($comment->uid == $variables['node']->uid) { $variables['classes_array'][] = 'comment-by-node-author'; } - if ($comment->uid === $variables['user']->uid) { + if ($comment->uid == $variables['user']->uid) { $variables['classes_array'][] = 'comment-by-viewer'; } - if ($variables['new']) { - $variables['classes_array'][] = 'comment-new'; - } } } diff --git a/modules/comment/comment.test b/modules/comment/comment.test index f4e57c110ccd2b215f52c259b4021cc803a0a52c..970cc83ae0af2e7f32c8d1a4dae23b074aa4b1a0 100644 --- a/modules/comment/comment.test +++ b/modules/comment/comment.test @@ -251,56 +251,6 @@ class CommentHelperCase extends DrupalWebTestCase { return $match[2]; } - - /** - * Tests new comment marker. - */ - public function testCommentNewCommentsIndicator() { - // Test if the right links are displayed when no comment is present for the - // node. - $this->drupalLogin($this->admin_user); - $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_OPEN)); - $this->drupalGet('node'); - $this->assertNoLink(t('@count comments', array('@count' => 0))); - $this->assertNoLink(t('@count new comments', array('@count' => 0))); - $this->assertLink(t('Read more')); - $count = $this->xpath('//div[@id=:id]/div[@class=:class]/ul/li', array(':id' => 'node-' . $this->node->nid, ':class' => 'link-wrapper')); - $this->assertTrue(count($count) == 1, t('One child found')); - - // Create a new comment. This helper function may be run with different - // comment settings so use comment_save() to avoid complex setup. - $comment = (object) array( - 'cid' => NULL, - 'nid' => $this->node->nid, - 'node_type' => $this->node->type, - 'pid' => 0, - 'uid' => $this->loggedInUser->uid, - 'status' => COMMENT_PUBLISHED, - 'subject' => $this->randomName(), - 'hostname' => ip_address(), - 'language' => LANGUAGE_NONE, - 'comment_body' => array(LANGUAGE_NONE => array($this->randomName())), - ); - comment_save($comment); - $this->drupalLogout(); - - // Log in with 'web user' and check comment links. - $this->drupalLogin($this->web_user); - $this->drupalGet('node'); - $this->assertLink(t('1 new comment')); - $this->clickLink(t('1 new comment')); - $this->assertRaw('<a id="new"></a>', t('Found "new" marker.')); - $this->assertTrue($this->xpath('//a[@id=:new]/following-sibling::a[1][@id=:comment_id]', array(':new' => 'new', ':comment_id' => 'comment-1')), t('The "new" anchor is positioned at the right comment.')); - - // Test if "new comment" link is correctly removed. - $this->drupalGet('node'); - $this->assertLink(t('1 comment')); - $this->assertLink(t('Read more')); - $this->assertNoLink(t('1 new comment')); - $this->assertNoLink(t('@count new comments', array('@count' => 0))); - $count = $this->xpath('//div[@id=:id]/div[@class=:class]/ul/li', array(':id' => 'node-' . $this->node->nid, ':class' => 'link-wrapper')); - $this->assertTrue(count($count) == 2, print_r($count, TRUE)); - } } class CommentInterfaceTest extends CommentHelperCase { @@ -331,8 +281,6 @@ class CommentInterfaceTest extends CommentHelperCase { $comment = $this->postComment($this->node, $comment_text); $comment_loaded = comment_load($comment->id); $this->assertTrue($this->commentExists($comment), t('Comment found.')); - $by_viewer_class = $this->xpath('//a[@id=:comment_id]/following-sibling::div[1][contains(@class, "comment-by-viewer")]', array(':comment_id' => 'comment-' . $comment->id)); - $this->assertTrue(!empty($by_viewer_class), t('HTML class for comments by viewer found.')); // Set comments to have subject and preview to required. $this->drupalLogout(); @@ -419,11 +367,6 @@ class CommentInterfaceTest extends CommentHelperCase { $this->assertTrue($this->commentExists($reply, TRUE), t('Page two exists. %s')); $this->setCommentsPerPage(50); - // Create comment #5 to assert HTML class. - $comment = $this->postComment($this->node, $this->randomName(), $this->randomName()); - $by_node_author_class = $this->xpath('//a[@id=:comment_id]/following-sibling::div[1][contains(@class, "comment-by-node-author")]', array(':comment_id' => 'comment-' . $comment->id)); - $this->assertTrue(!empty($by_node_author_class), t('HTML class for node author found.')); - // Attempt to post to node with comments disabled. $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_HIDDEN)); $this->assertTrue($this->node, t('Article node created.')); @@ -472,6 +415,163 @@ class CommentInterfaceTest extends CommentHelperCase { $this->setCommentForm(FALSE); } + /** + * Tests new comment marker. + */ + public function testCommentNewCommentsIndicator() { + // Test if the right links are displayed when no comment is present for the + // node. + $this->drupalLogin($this->admin_user); + $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_OPEN)); + $this->drupalGet('node'); + $this->assertNoLink(t('@count comments', array('@count' => 0))); + $this->assertNoLink(t('@count new comments', array('@count' => 0))); + $this->assertLink(t('Read more')); + $count = $this->xpath('//div[@id=:id]/div[@class=:class]/ul/li', array(':id' => 'node-' . $this->node->nid, ':class' => 'link-wrapper')); + $this->assertTrue(count($count) == 1, t('One child found')); + + // Create a new comment. This helper function may be run with different + // comment settings so use comment_save() to avoid complex setup. + $comment = (object) array( + 'cid' => NULL, + 'nid' => $this->node->nid, + 'node_type' => $this->node->type, + 'pid' => 0, + 'uid' => $this->loggedInUser->uid, + 'status' => COMMENT_PUBLISHED, + 'subject' => $this->randomName(), + 'hostname' => ip_address(), + 'language' => LANGUAGE_NONE, + 'comment_body' => array(LANGUAGE_NONE => array($this->randomName())), + ); + comment_save($comment); + $this->drupalLogout(); + + // Log in with 'web user' and check comment links. + $this->drupalLogin($this->web_user); + $this->drupalGet('node'); + $this->assertLink(t('1 new comment')); + $this->clickLink(t('1 new comment')); + $this->assertRaw('<a id="new"></a>', t('Found "new" marker.')); + $this->assertTrue($this->xpath('//a[@id=:new]/following-sibling::a[1][@id=:comment_id]', array(':new' => 'new', ':comment_id' => 'comment-1')), t('The "new" anchor is positioned at the right comment.')); + + // Test if "new comment" link is correctly removed. + $this->drupalGet('node'); + $this->assertLink(t('1 comment')); + $this->assertLink(t('Read more')); + $this->assertNoLink(t('1 new comment')); + $this->assertNoLink(t('@count new comments', array('@count' => 0))); + $count = $this->xpath('//div[@id=:id]/div[@class=:class]/ul/li', array(':id' => 'node-' . $this->node->nid, ':class' => 'link-wrapper')); + $this->assertTrue(count($count) == 2, print_r($count, TRUE)); + } + + /** + * Tests CSS classes on comments. + */ + function testCommentClasses() { + // Create all permutations for comments, users, and nodes. + $parameters = array( + 'node_uid' => array(0, $this->web_user->uid), + 'comment_uid' => array(0, $this->web_user->uid, $this->admin_user->uid), + 'comment_status' => array(COMMENT_PUBLISHED, COMMENT_NOT_PUBLISHED), + 'user' => array('anonymous', 'authenticated', 'admin'), + ); + $permutations = $this->generatePermutations($parameters); + + foreach ($permutations as $case) { + // Create a new node. + $node = $this->drupalCreateNode(array('type' => 'article', 'uid' => $case['node_uid'])); + + // Add a comment. + $comment = (object) array( + 'cid' => NULL, + 'nid' => $node->nid, + 'pid' => 0, + 'uid' => $case['comment_uid'], + 'status' => $case['comment_status'], + 'subject' => $this->randomName(), + 'language' => LANGUAGE_NONE, + 'comment_body' => array(LANGUAGE_NONE => array($this->randomName())), + ); + comment_save($comment); + + // Adjust the current/viewing user. + switch ($case['user']) { + case 'anonymous': + $this->drupalLogout(); + $case['user_uid'] = 0; + break; + + case 'authenticated': + $this->drupalLogin($this->web_user); + $case['user_uid'] = $this->web_user->uid; + break; + + case 'admin': + $this->drupalLogin($this->admin_user); + $case['user_uid'] = $this->admin_user->uid; + break; + } + // Request the node with the comment. + $this->drupalGet('node/' . $node->nid); + + // Verify classes if the comment is visible for the current user. + if ($case['comment_status'] == COMMENT_PUBLISHED || $case['user'] == 'admin') { + // Verify the comment-by-anonymous class. + $comments = $this->xpath('//*[contains(@class, "comment-by-anonymous")]'); + if ($case['comment_uid'] == 0) { + $this->assertTrue(count($comments) == 1, 'comment-by-anonymous class found.'); + } + else { + $this->assertFalse(count($comments), 'comment-by-anonymous class not found.'); + } + + // Verify the comment-by-node-author class. + $comments = $this->xpath('//*[contains(@class, "comment-by-node-author")]'); + if ($case['comment_uid'] > 0 && $case['comment_uid'] == $case['node_uid']) { + $this->assertTrue(count($comments) == 1, 'comment-by-node-author class found.'); + } + else { + $this->assertFalse(count($comments), 'comment-by-node-author class not found.'); + } + + // Verify the comment-by-viewer class. + $comments = $this->xpath('//*[contains(@class, "comment-by-viewer")]'); + if ($case['comment_uid'] > 0 && $case['comment_uid'] == $case['user_uid']) { + $this->assertTrue(count($comments) == 1, 'comment-by-viewer class found.'); + } + else { + $this->assertFalse(count($comments), 'comment-by-viewer class not found.'); + } + } + + // Verify the comment-unpublished class. + $comments = $this->xpath('//*[contains(@class, "comment-unpublished")]'); + if ($case['comment_status'] == COMMENT_NOT_PUBLISHED && $case['user'] == 'admin') { + $this->assertTrue(count($comments) == 1, 'comment-unpublished class found.'); + } + else { + $this->assertFalse(count($comments), 'comment-unpublished class not found.'); + } + + // Verify the comment-new class. + if ($case['comment_status'] == COMMENT_PUBLISHED || $case['user'] == 'admin') { + $comments = $this->xpath('//*[contains(@class, "comment-new")]'); + if ($case['user'] != 'anonymous') { + $this->assertTrue(count($comments) == 1, 'comment-new class found.'); + + // Request the node again. The comment-new class should disappear. + $this->drupalGet('node/' . $node->nid); + $comments = $this->xpath('//*[contains(@class, "comment-new")]'); + $this->assertFalse(count($comments), 'comment-new class not found.'); + } + else { + $this->assertFalse(count($comments), 'comment-new class not found.'); + } + } + } + } + /** * Tests the node comment statistics. */ @@ -970,8 +1070,6 @@ class CommentAnonymous extends CommentHelperCase { // Post anonymous comment without contact info. $anonymous_comment1 = $this->postComment($this->node, $this->randomName(), $this->randomName()); $this->assertTrue($this->commentExists($anonymous_comment1), t('Anonymous comment without contact info found.')); - $anonymous_class = $this->xpath('//a[@id=:comment_id]/following-sibling::div[1][contains(@class, "comment-by-anonymous")]', array(':comment_id' => 'comment-' . $anonymous_comment1->id)); - $this->assertTrue(!empty($anonymous_class), t('HTML class for anonymous comments found.')); // Allow contact info. $this->drupalLogin($this->admin_user); diff --git a/modules/contact/contact.admin.inc b/modules/contact/contact.admin.inc index 9fde037d3454c7ccecea41c69bdd6ac795725b48..4648fd3d764d83059fc1fba4f64ba3408567754e 100644 --- a/modules/contact/contact.admin.inc +++ b/modules/contact/contact.admin.inc @@ -2,7 +2,7 @@ /** * @file - * Admin page callbacks for the contact module. + * Admin page callbacks for the Contact module. */ /** @@ -53,7 +53,22 @@ function contact_category_list() { } /** - * Category edit page. + * Form constructor for the category edit form. + * + * @param $category + * An array describing the category to be edited. May be empty for new + * categories. Recognized array keys are: + * - category: The name of the category. + * - recipients: A comma-separated list of recipients. + * - reply: (optional) The body of the auto-reply message. + * - weight: The weight of the category. + * - selected: Boolean indicating whether the category should be selected by + * default. + * - cid: The category ID for which the form is to be displayed. + * + * @see contact_category_edit_form_validate() + * @see contact_category_edit_form_submit() + * @ingroup forms */ function contact_category_edit_form($form, &$form_state, array $category = array()) { // If this is a new category, add the default values. @@ -117,7 +132,9 @@ function contact_category_edit_form($form, &$form_state, array $category = array } /** - * Validate the contact category edit page form submission. + * Form validation handler for contact_category_edit_form(). + * + * @see contact_category_edit_form_submit() */ function contact_category_edit_form_validate($form, &$form_state) { // Validate and each e-mail recipient. @@ -144,7 +161,9 @@ function contact_category_edit_form_validate($form, &$form_state) { } /** - * Process the contact category edit page form submission. + * Form submission handler for contact_category_edit_form(). + * + * @see contact_category_edit_form_validate() */ function contact_category_edit_form_submit($form, &$form_state) { if ($form_state['values']['selected']) { @@ -167,8 +186,13 @@ function contact_category_edit_form_submit($form, &$form_state) { } /** - * Form builder for deleting a contact category. + * Form constructor for the contact category deletion form. + * + * @param $contact + * Array describing the contact category to be deleted. See the documentation + * of contact_category_edit_form() for the recognized keys. * + * @see contact_menu() * @see contact_category_delete_form_submit() */ function contact_category_delete_form($form, &$form_state, array $contact) { @@ -188,9 +212,7 @@ function contact_category_delete_form($form, &$form_state, array $contact) { } /** - * Submit handler for the confirm delete category form. - * - * @see contact_category_delete_form() + * Form submission handler for contact_category_delete_form(). */ function contact_category_delete_form_submit($form, &$form_state) { $contact = $form['contact']['#value']; diff --git a/modules/contact/contact.info b/modules/contact/contact.info index 4efaed933d0f24cab972437ce03f52f0d55bd885..fd41564f2adcdf98d3c5b4d4870254ecc61a9b86 100644 --- a/modules/contact/contact.info +++ b/modules/contact/contact.info @@ -6,8 +6,8 @@ core = 7.x files[] = contact.test configure = admin/structure/contact -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/contact/contact.module b/modules/contact/contact.module index eaae9c62c68988a5ff85abdcfd0e831011542eca..a8701827dbfcb33f93a93790b5c5ff37ed369220 100644 --- a/modules/contact/contact.module +++ b/modules/contact/contact.module @@ -110,10 +110,7 @@ function contact_menu() { * Menu access callback for a user's personal contact form. * * @param $account - * A user account object. - * @return - * TRUE if the current user has access to the requested user's contact form, - * or FALSE otherwise. + * The user object of the user whose contact form is being requested. */ function _contact_personal_tab_access($account) { global $user; @@ -148,10 +145,11 @@ function _contact_personal_tab_access($account) { } /** - * Load a contact category. + * Loads a contact category. * * @param $cid * The contact category ID. + * * @return * An array with the contact category's data. */ @@ -211,6 +209,8 @@ function contact_mail($key, &$message, $params) { * Implements hook_form_FORM_ID_alter(). * * Add the enable personal contact form to an individual user's account page. + * + * @see user_profile_form() */ function contact_form_user_profile_form_alter(&$form, &$form_state) { if ($form['#user_category'] == 'account') { @@ -241,6 +241,8 @@ function contact_user_presave(&$edit, $account, $category) { * Implements hook_form_FORM_ID_alter(). * * Add the default personal contact setting on the user settings page. + * + * @see user_admin_settings() */ function contact_form_user_admin_settings_alter(&$form, &$form_state) { $form['contact'] = array( diff --git a/modules/contact/contact.pages.inc b/modules/contact/contact.pages.inc index 30b2825045fc6f5a8f3ecc85276f4d5a4497d214..ba8918bf517df289141f5a84dd173126c60f3757 100644 --- a/modules/contact/contact.pages.inc +++ b/modules/contact/contact.pages.inc @@ -2,14 +2,15 @@ /** * @file - * User page callbacks for the contact module. + * Page callbacks for the Contact module. */ /** - * Form builder; the site-wide contact form. + * Form constructor for the site-wide contact form. * * @see contact_site_form_validate() * @see contact_site_form_submit() + * @ingroup forms */ function contact_site_form($form, &$form_state) { global $user; @@ -112,6 +113,8 @@ function contact_site_form($form, &$form_state) { /** * Form validation handler for contact_site_form(). + * + * @see contact_site_form_submit() */ function contact_site_form_validate($form, &$form_state) { if (!$form_state['values']['cid']) { @@ -124,6 +127,8 @@ function contact_site_form_validate($form, &$form_state) { /** * Form submission handler for contact_site_form(). + * + * @see contact_site_form_validate() */ function contact_site_form_submit($form, &$form_state) { global $user, $language; @@ -166,10 +171,14 @@ function contact_site_form_submit($form, &$form_state) { } /** - * Form builder; the personal contact form. + * Form constructor for the personal contact form. * + * Path: user/%user/contact + * + * @see contact_menu() * @see contact_personal_form_validate() * @see contact_personal_form_submit() + * @ingroup forms */ function contact_personal_form($form, &$form_state, $recipient) { global $user; @@ -244,7 +253,7 @@ function contact_personal_form($form, &$form_state, $recipient) { /** * Form validation handler for contact_personal_form(). * - * @see contact_personal_form() + * @see contact_personal_form_submit() */ function contact_personal_form_validate($form, &$form_state) { if (!valid_email_address($form_state['values']['mail'])) { @@ -255,7 +264,7 @@ function contact_personal_form_validate($form, &$form_state) { /** * Form submission handler for contact_personal_form(). * - * @see contact_personal_form() + * @see contact_personal_form_validate() */ function contact_personal_form_submit($form, &$form_state) { global $user, $language; diff --git a/modules/contact/contact.test b/modules/contact/contact.test index 129eb30ce34320d29400d610104da242ae625935..48c8bb01fd63a0c0172adabf0c368c252ae235d3 100644 --- a/modules/contact/contact.test +++ b/modules/contact/contact.test @@ -1,10 +1,12 @@ <?php - /** - * @file - * Tests for contact.module. + * @file + * Tests for the Contact module. */ +/** + * Tests the site-wide contact form. + */ class ContactSitewideTestCase extends DrupalWebTestCase { public static function getInfo() { return array( @@ -19,7 +21,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Test configuration options and site-wide contact form. + * Tests configuration options and the site-wide contact form. */ function testSiteWideContact() { // Create and login administrative user. @@ -159,7 +161,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Test auto-reply on the site-wide contact form. + * Tests auto-reply on the site-wide contact form. */ function testAutoReply() { // Create and login administrative user. @@ -200,12 +202,17 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Add a category. + * Adds a category. * - * @param string $category Name of category. - * @param string $recipients List of recipient e-mail addresses. - * @param string $reply Auto-reply text. - * @param boolean $selected Defautly selected. + * @param string $category + * The category name. + * @param string $recipients + * The list of recipient e-mail addresses. + * @param string $reply + * The auto-reply text that is sent to a user upon completing the contact + * form. + * @param boolean $selected + * Boolean indicating whether the category should be selected by default. */ function addCategory($category, $recipients, $reply, $selected) { $edit = array(); @@ -217,12 +224,17 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Update a category. + * Updates a category. * - * @param string $category Name of category. - * @param string $recipients List of recipient e-mail addresses. - * @param string $reply Auto-reply text. - * @param boolean $selected Defautly selected. + * @param string $category + * The category name. + * @param string $recipients + * The list of recipient e-mail addresses. + * @param string $reply + * The auto-reply text that is sent to a user upon completing the contact + * form. + * @param boolean $selected + * Boolean indicating whether the category should be selected by default. */ function updateCategory($categories, $category, $recipients, $reply, $selected) { $category_id = $categories[array_rand($categories)]; @@ -236,13 +248,18 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Submit contact form. + * Submits the contact form. * - * @param string $name Name. - * @param string $mail E-mail address. - * @param string $subject Subject. - * @param integer $cid Category id. - * @param string $message Message. + * @param string $name + * The name of the sender. + * @param string $mail + * The e-mail address of the sender. + * @param string $subject + * The subject of the message. + * @param integer $cid + * The category ID of the message. + * @param string $message + * The message body. */ function submitContact($name, $mail, $subject, $cid, $message) { $edit = array(); @@ -255,7 +272,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Delete all categories. + * Deletes all categories. */ function deleteCategories() { $categories = $this->getCategories(); @@ -267,9 +284,10 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Get list category ids. + * Gets a list of all category IDs. * - * @return array Category ids. + * @return array + * A list of the category IDs. */ function getCategories() { $categories = db_query('SELECT cid FROM {contact}')->fetchCol(); @@ -278,7 +296,7 @@ class ContactSitewideTestCase extends DrupalWebTestCase { } /** - * Test the personal contact form. + * Tests the personal contact form. */ class ContactPersonalTestCase extends DrupalWebTestCase { private $admin_user; @@ -306,7 +324,7 @@ class ContactPersonalTestCase extends DrupalWebTestCase { } /** - * Test personal contact form access. + * Tests access to the personal contact form. */ function testPersonalContactAccess() { // Test allowed access to user with contact form enabled. @@ -369,7 +387,7 @@ class ContactPersonalTestCase extends DrupalWebTestCase { } /** - * Test the personal contact form flood protection. + * Tests the personal contact form flood protection. */ function testPersonalContactFlood() { $flood_limit = 3; @@ -399,7 +417,7 @@ class ContactPersonalTestCase extends DrupalWebTestCase { } /** - * Fill out a user's personal contact form and submit. + * Fills out a user's personal contact form and submits it. * * @param $account * A user object of the user being contacted. diff --git a/modules/contextual/contextual.info b/modules/contextual/contextual.info index e6fdace09a544d8423b3dff5f543d520363563eb..c4c8418f139629413146c09e3f3453faa9fc0c80 100644 --- a/modules/contextual/contextual.info +++ b/modules/contextual/contextual.info @@ -4,8 +4,8 @@ package = Core version = VERSION core = 7.x -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/dashboard/dashboard.info b/modules/dashboard/dashboard.info index 294c0257697631867454dbd96090674c7f1df0f3..43227ad08680d7c34ad10f03a523469bc4abb81e 100644 --- a/modules/dashboard/dashboard.info +++ b/modules/dashboard/dashboard.info @@ -7,8 +7,8 @@ files[] = dashboard.test dependencies[] = block configure = admin/dashboard/customize -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/dblog/dblog.info b/modules/dblog/dblog.info index 7fa0931aff40ec2f6c7a1f8b9ac4dec668d3653e..cde11a8695d88a0b380e804b4ad099318badbabe 100644 --- a/modules/dblog/dblog.info +++ b/modules/dblog/dblog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = dblog.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/field.api.php b/modules/field/field.api.php index d0d5da9c68db4af97b3d16c2bbc7e0a1c3e0d30b..329cf16d126ac0dab26b5ceec0443016b03676b6 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -1,7 +1,7 @@ <?php /** - * @ingroup field_fieldable_type + * @ingroup hooks * @{ */ @@ -80,10 +80,6 @@ function hook_field_extra_fields_alter(&$info) { } } -/** - * @} End of "ingroup field_fieldable_type" - */ - /** * @defgroup field_types Field Types API * @{ @@ -119,6 +115,9 @@ function hook_field_extra_fields_alter(&$info) { * * A third kind of pluggable handlers, storage backends, is defined by the * @link field_storage Field Storage API @endlink. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -706,6 +705,7 @@ function hook_field_is_empty($item, $field) { * @see hook_field_widget_form_alter() * @see hook_field_widget_WIDGET_TYPE_form_alter() * @see hook_field_widget_error() + * @see hook_field_widget_settings_form() */ function hook_field_widget_info() { return array( @@ -1505,10 +1505,6 @@ function hook_field_attach_delete_bundle($entity_type, $bundle, $instances) { * @} End of "ingroup field_attach" */ -/********************************************************************** - * Field Storage API - **********************************************************************/ - /** * @ingroup field_storage * @{ @@ -2346,10 +2342,6 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) { * @} End of "ingroup field_storage" */ -/********************************************************************** - * Field CRUD API - **********************************************************************/ - /** * @ingroup field_crud * @{ @@ -2603,10 +2595,6 @@ function hook_field_storage_purge($entity_type, $entity, $field, $instance) { * @} End of "ingroup field_crud" */ -/********************************************************************** - * TODO: I'm not sure where these belong yet. - **********************************************************************/ - /** * Determine whether the user has access to a given field. * @@ -2633,3 +2621,7 @@ function hook_field_access($op, $field, $entity_type, $entity, $account) { } return TRUE; } + +/** + * @} End of "ingroup hooks" + */ diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 2419201dea79ed52951755f982bc337c78bef9cc..bd2934b485b66e11563ba623fda0937c2efe6699 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -44,6 +44,9 @@ class FieldValidationException extends FieldException { * * Each field defines which storage backend it uses. The Drupal system variable * 'field_storage_default' identifies the storage backend used by default. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -117,6 +120,12 @@ define('FIELD_STORAGE_INSERT', 'insert'); * The pre-operation hooks do not make the Field Storage API irrelevant. The * Field Storage API is essentially the "fallback mechanism" for any fields * that aren't being intercepted explicitly by pre-operation hooks. + * + * @link field_language Field Language API @endlink provides information about + * the structure of field objects. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -185,8 +194,10 @@ function _field_invoke($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $opti // Iterate through the instances and collect results. $return = array(); foreach ($instances as $instance) { - $field_name = $instance['field_name']; - $field = field_info_field($field_name); + // field_info_field() is not available for deleted fields, so use + // field_info_field_by_id(). + $field = field_info_field_by_id($instance['field_id']); + $field_name = $field['field_name']; $function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op; if (function_exists($function)) { // Determine the list of languages to iterate on. @@ -694,7 +705,8 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $ } // Invoke field-type module's hook_field_load(). - _field_invoke_multiple('load', $entity_type, $queried_entities, $age, $options); + $null = NULL; + _field_invoke_multiple('load', $entity_type, $queried_entities, $age, $null, $options); // Invoke hook_field_attach_load(): let other modules act on loading the // entitiy. diff --git a/modules/field/field.crud.inc b/modules/field/field.crud.inc index e34c0c5282b2d4185857ce377a6e82b6b1788f01..6df32352bf20d67b4114d436860f2de39510974a 100644 --- a/modules/field/field.crud.inc +++ b/modules/field/field.crud.inc @@ -15,6 +15,9 @@ * * The Field CRUD API uses * @link field Field API data structures @endlink. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -42,10 +45,11 @@ * system variable. * - settings: each omitted setting is given the default value specified in * hook_field_storage_info(). + * * @return * The $field array with the id property filled in. - * @throw - * FieldException + * + * @throws FieldException * * See: @link field Field API data structures @endlink. */ @@ -442,8 +446,8 @@ function field_delete_field($field_name) { * * @return * The $instance array with the id property filled in. - * @throw - * FieldException + * + * @throws FieldException * * See: @link field Field API data structures @endlink. */ @@ -510,8 +514,7 @@ function field_create_instance($instance) { * properties specified in $instance overwrite the existing values for * the instance. * - * @throw - * FieldException + * @throws FieldException * * @see field_create_instance() */ @@ -824,6 +827,9 @@ function field_delete_instance($instance, $field_cleanup = TRUE) { * ), * ); * @endcode + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** diff --git a/modules/field/field.info b/modules/field/field.info index 15cab0e78c4c67abe7af528df5a89a6195e8f097..3a377477b8b91c86b6d3582f40fc0c6d3e58d76d 100644 --- a/modules/field/field.info +++ b/modules/field/field.info @@ -10,8 +10,8 @@ dependencies[] = field_sql_storage required = TRUE stylesheets[all][] = theme/field.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index 6b172dd3417b724d366bb804d0b44c633b0f6036..3b36c03550fbbfefd91ee87af4cfb7ba5db13ce0 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -13,6 +13,9 @@ * The Field Info API exposes information about field types, fields, * instances, bundles, widget types, display formatters, behaviors, * and settings defined by or with the Field API. + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** @@ -24,6 +27,7 @@ */ function field_info_cache_clear() { drupal_static_reset('field_view_mode_settings'); + drupal_static_reset('field_available_languages'); // @todo: Remove this when field_attach_*_bundle() bundle management // functions are moved to the entity API. @@ -602,8 +606,9 @@ function field_info_fields() { * * @param $field_name * The name of the field to retrieve. $field_name can only refer to a - * non-deleted, active field. Use field_read_fields() to retrieve information - * on deleted or inactive fields. + * non-deleted, active field. For deleted fields, use + * field_info_field_by_id(). To retrieve information about inactive fields, + * use field_read_fields(). * * @return * The field array, as returned by field_read_fields(), with an @@ -624,7 +629,7 @@ function field_info_field($field_name) { * * @param $field_id * The id of the field to retrieve. $field_id can refer to a - * deleted field. + * deleted field, but not an inactive one. * * @return * The field array, as returned by field_read_fields(), with an diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc index 5373d970828439cc24628992fc959c1294269e91..4b4f01d7db847ba383a1fe3b53f4f2569d3b7a4f 100644 --- a/modules/field/field.multilingual.inc +++ b/modules/field/field.multilingual.inc @@ -57,6 +57,9 @@ * even disabled by modules implementing hook_field_language_alter(), making * it possible to choose the first approach. The display language for each * field is returned by field_language(). + * + * See @link field Field API @endlink for information about the other parts of + * the Field API. */ /** diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.info b/modules/field/modules/field_sql_storage/field_sql_storage.info index dcf342b1f04c45af8739d9b865ee5d79ce3773bd..a1a6681b6ed56c46ba8a4f4d820d316bce85bb06 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.info +++ b/modules/field/modules/field_sql_storage/field_sql_storage.info @@ -7,8 +7,8 @@ dependencies[] = field files[] = field_sql_storage.test required = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/modules/list/list.info b/modules/field/modules/list/list.info index f470ee2e96d79939dc424739ceb2959507f1c607..285340ac7e7f059bcc586399cd23899c97455aa8 100644 --- a/modules/field/modules/list/list.info +++ b/modules/field/modules/list/list.info @@ -7,8 +7,8 @@ dependencies[] = field dependencies[] = options files[] = tests/list.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/modules/list/tests/list_test.info b/modules/field/modules/list/tests/list_test.info index 78cd65af1b601c7932912f208a73ce01f1b4577b..01011b31ad0ba92c479cf5c85e715728d5534c41 100644 --- a/modules/field/modules/list/tests/list_test.info +++ b/modules/field/modules/list/tests/list_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/modules/number/number.info b/modules/field/modules/number/number.info index 78fa8edcf11525e81b74a5989f228bf97ac339e5..93c9b880a15744491fa7682d9488f7d067ba0f33 100644 --- a/modules/field/modules/number/number.info +++ b/modules/field/modules/number/number.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = number.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/modules/options/options.info b/modules/field/modules/options/options.info index d39c18648b8f40827c0418e42207ad1e93493a48..f51cd1bdfbfb3dad33e0fa3e9bd71bf0bc34210b 100644 --- a/modules/field/modules/options/options.info +++ b/modules/field/modules/options/options.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = options.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/modules/text/text.info b/modules/field/modules/text/text.info index 2460e5cbf86ebd37784f595887cb3861df8aa99d..1ae0854d8f9f70e0686c47e1e4e427d61357e039 100644 --- a/modules/field/modules/text/text.info +++ b/modules/field/modules/text/text.info @@ -7,8 +7,8 @@ dependencies[] = field files[] = text.test required = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 669fc37cf44104ca07fe06a2ae6e9a5fa34f9ac2..657f1f36422d81580fc522c81dab6e32f464ed6b 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -72,12 +72,13 @@ class FieldTestCase extends DrupalWebTestCase { } class FieldAttachTestCase extends FieldTestCase { - function setUp($modules = array()) { + function setUp() { // Since this is a base class for many test cases, support the same // flexibility that DrupalWebTestCase::setUp() has for the modules to be // passed in as either an array or a variable number of string arguments. - if (!is_array($modules)) { - $modules = func_get_args(); + $modules = func_get_args(); + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; } if (!in_array('field_test', $modules)) { $modules[] = 'field_test'; @@ -2674,7 +2675,7 @@ class FieldTranslationsTestCase extends FieldTestCase { // Test field_available_languages() behavior for untranslatable fields. $this->field['translatable'] = FALSE; - $this->field_name = $this->field['field_name'] = $this->instance['field_name'] = drupal_strtolower($this->randomName() . '_field_name'); + field_update_field($this->field); $available_languages = field_available_languages($this->entity_type, $this->field); $this->assertTrue(count($available_languages) == 1 && $available_languages[0] === LANGUAGE_NONE, t('For untranslatable fields only LANGUAGE_NONE is available.')); } @@ -2989,22 +2990,54 @@ class FieldBulkDeleteTestCase extends FieldTestCase { */ function _generateStubEntities($entity_type, $entities, $field_name = NULL) { $stubs = array(); - foreach ($entities as $entity) { + foreach ($entities as $id => $entity) { $stub = entity_create_stub_entity($entity_type, entity_extract_ids($entity_type, $entity)); if (isset($field_name)) { $stub->{$field_name} = $entity->{$field_name}; } - $stubs[] = $stub; + $stubs[$id] = $stub; } return $stubs; } + /** + * Tests that the expected hooks have been invoked on the expected entities. + * + * @param $expected_hooks + * An array keyed by hook name, with one entry per expected invocation. + * Each entry is the value of the "$entity" parameter the hook is expected + * to have been passed. + * @param $actual_hooks + * The array of actual hook invocations recorded by field_test_memorize(). + */ + function checkHooksInvocations($expected_hooks, $actual_hooks) { + foreach ($expected_hooks as $hook => $invocations) { + $actual_invocations = $actual_hooks[$hook]; + + // Check that the number of invocations is correct. + $this->assertEqual(count($actual_invocations), count($invocations), "$hook() was called the expected number of times."); + + // Check that the hook was called for each expected argument. + foreach ($invocations as $argument) { + $found = FALSE; + foreach ($actual_invocations as $actual_arguments) { + if ($actual_arguments[1] == $argument) { + $found = TRUE; + break; + } + } + $this->assertTrue($found, "$hook() was called on expected argument"); + } + } + } + function setUp() { parent::setUp('field_test'); - // Clean up data from previous test cases. $this->fields = array(); $this->instances = array(); + $this->entities = array(); + $this->entities_by_bundles = array(); // Create two bundles. $this->bundles = array('bb_1' => 'bb_1', 'bb_2' => 'bb_2'); @@ -3040,7 +3073,10 @@ class FieldBulkDeleteTestCase extends FieldTestCase { foreach ($this->fields as $field) { $entity->{$field['field_name']}[LANGUAGE_NONE] = $this->_generateTestFieldValues($field['cardinality']); } + $this->entities[$id] = $entity; + // Also keep track of the entities per bundle. + $this->entities_by_bundles[$bundle][$id] = $entity; field_attach_insert($this->entity_type, $entity); $id++; } @@ -3105,6 +3141,7 @@ class FieldBulkDeleteTestCase extends FieldTestCase { * instance is deleted. */ function testPurgeInstance() { + // Start recording hook invocations. field_test_memorize(); $bundle = reset($this->bundles); @@ -3119,7 +3156,7 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertEqual(count($mem), 0, 'No field hooks were called'); $batch_size = 2; - for ($count = 8; $count >= 0; $count -= 2) { + for ($count = 8; $count >= 0; $count -= $batch_size) { // Purge two entities. field_purge_batch($batch_size); @@ -3133,19 +3170,21 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertEqual($count ? count($found['test_entity']) : count($found), $count, 'Correct number of entities found after purging 2'); } - // hook_field_delete() was called on a pseudo-entity for each entity. Each - // pseudo entity has a $field property that matches the original entity, - // but no others. - $mem = field_test_memorize(); - $this->assertEqual(count($mem['field_test_field_delete']), 10, 'hook_field_delete was called for the right number of entities'); - $stubs = $this->_generateStubEntities($this->entity_type, $this->entities, $field['field_name']); - $count = count($stubs); - foreach ($mem['field_test_field_delete'] as $args) { - $entity = $args[1]; - $this->assertEqual($stubs[$entity->ftid], $entity, 'hook_field_delete() called with the correct stub'); - unset($stubs[$entity->ftid]); + // Check hooks invocations. + // - hook_field_load() (multiple hook) should have been called on all + // entities by pairs of two. + // - hook_field_delete() should have been called once for each entity in the + // bundle. + $actual_hooks = field_test_memorize(); + $hooks = array(); + $stubs = $this->_generateStubEntities($this->entity_type, $this->entities_by_bundles[$bundle], $field['field_name']); + foreach (array_chunk($stubs, $batch_size, TRUE) as $chunk) { + $hooks['field_test_field_load'][] = $chunk; } - $this->assertEqual(count($stubs), $count-10, 'hook_field_delete was called with each entity once'); + foreach ($stubs as $stub) { + $hooks['field_test_field_delete'][] = $stub; + } + $this->checkHooksInvocations($hooks, $actual_hooks); // The instance still exists, deleted. $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1)); @@ -3168,15 +3207,37 @@ class FieldBulkDeleteTestCase extends FieldTestCase { * instances are deleted and purged. */ function testPurgeField() { + // Start recording hook invocations. + field_test_memorize(); + $field = reset($this->fields); // Delete the first instance. - $instance = field_info_instance($this->entity_type, $field['field_name'], 'bb_1'); + $bundle = reset($this->bundles); + $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); field_delete_instance($instance); + // Assert that hook_field_delete() was not called yet. + $mem = field_test_memorize(); + $this->assertEqual(count($mem), 0, 'No field hooks were called.'); + // Purge the data. field_purge_batch(10); + // Check hooks invocations. + // - hook_field_load() (multiple hook) should have been called once, for all + // entities in the bundle. + // - hook_field_delete() should have been called once for each entity in the + // bundle. + $actual_hooks = field_test_memorize(); + $hooks = array(); + $stubs = $this->_generateStubEntities($this->entity_type, $this->entities_by_bundles[$bundle], $field['field_name']); + $hooks['field_test_field_load'][] = $stubs; + foreach ($stubs as $stub) { + $hooks['field_test_field_delete'][] = $stub; + } + $this->checkHooksInvocations($hooks, $actual_hooks); + // Purge again to purge the instance. field_purge_batch(0); @@ -3185,12 +3246,27 @@ class FieldBulkDeleteTestCase extends FieldTestCase { $this->assertTrue(isset($fields[$field['id']]) && !$fields[$field['id']]['deleted'], 'The field exists and is not deleted'); // Delete the second instance. - $instance = field_info_instance($this->entity_type, $field['field_name'], 'bb_2'); + $bundle = next($this->bundles); + $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); field_delete_instance($instance); + // Assert that hook_field_delete() was not called yet. + $mem = field_test_memorize(); + $this->assertEqual(count($mem), 0, 'No field hooks were called.'); + // Purge the data. field_purge_batch(10); + // Check hooks invocations (same as above, for the 2nd bundle). + $actual_hooks = field_test_memorize(); + $hooks = array(); + $stubs = $this->_generateStubEntities($this->entity_type, $this->entities_by_bundles[$bundle], $field['field_name']); + $hooks['field_test_field_load'][] = $stubs; + foreach ($stubs as $stub) { + $hooks['field_test_field_delete'][] = $stub; + } + $this->checkHooksInvocations($hooks, $actual_hooks); + // The field still exists, deleted. $fields = field_read_fields(array('id' => $field['id']), array('include_deleted' => 1)); $this->assertTrue(isset($fields[$field['id']]) && $fields[$field['id']]['deleted'], 'The field exists and is deleted'); diff --git a/modules/field/tests/field_test.entity.inc b/modules/field/tests/field_test.entity.inc index b7c70a67711505e8effaf2e09cbab87ab433e3d2..52ed3fcd0988cb2c458acd6e0d0967c7d921a1b8 100644 --- a/modules/field/tests/field_test.entity.inc +++ b/modules/field/tests/field_test.entity.inc @@ -58,7 +58,7 @@ function field_test_entity_info() { 'id' => 'ftid', 'bundle' => 'fttype', ), - 'bundles' => array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2')), + 'bundles' => array('bundle1' => array('label' => 'Bundle1'), 'bundle2' => array('label' => 'Bundle2')) + $bundles, 'view modes' => $test_entity_modes, ), // In this case, the bundle key is not stored in the database. @@ -72,7 +72,7 @@ function field_test_entity_info() { 'id' => 'ftid', 'bundle' => 'fttype', ), - 'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')), + 'bundles' => array('test_entity_2' => array('label' => 'Test entity 2')) + $bundles, 'view modes' => $test_entity_modes, ), // @see EntityPropertiesTestCase::testEntityLabel() diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index b8a2939d64adeabac55595a9e15c8eafbdfd7b2e..cc76a998d955a9fece26561c6e1de6674d447693 100644 --- a/modules/field/tests/field_test.field.inc +++ b/modules/field/tests/field_test.field.inc @@ -58,6 +58,9 @@ function field_test_field_update_forbid($field, $prior_field, $has_data) { * Implements hook_field_load(). */ function field_test_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); + foreach ($items as $id => $item) { // To keep the test non-intrusive, only act for instances with the // test_hook_field_load setting explicitly set to TRUE. @@ -72,6 +75,30 @@ function field_test_field_load($entity_type, $entities, $field, $instances, $lan } } +/** + * Implements hook_field_insert(). + */ +function field_test_field_insert($entity_type, $entity, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Implements hook_field_update(). + */ +function field_test_field_update($entity_type, $entity, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + +/** + * Implements hook_field_delete(). + */ +function field_test_field_delete($entity_type, $entity, $field, $instance, $items) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); +} + /** * Implements hook_field_validate(). * @@ -79,6 +106,9 @@ function field_test_field_load($entity_type, $entities, $field, $instances, $lan * - 'field_test_invalid': The value is invalid. */ function field_test_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { + $args = func_get_args(); + field_test_memorize(__FUNCTION__, $args); + foreach ($items as $delta => $item) { if ($item['value'] == -1) { $errors[$field['field_name']][$langcode][$delta][] = array( @@ -350,11 +380,13 @@ function field_test_field_formatter_view($entity_type, $entity, $field, $instanc break; case 'field_test_multiple': - $array = array(); - foreach ($items as $delta => $item) { - $array[] = $delta . ':' . $item['value']; + if (!empty($items)) { + $array = array(); + foreach ($items as $delta => $item) { + $array[] = $delta . ':' . $item['value']; + } + $element[0] = array('#markup' => $settings['test_formatter_setting_multiple'] . '|' . implode('|', $array)); } - $element[0] = array('#markup' => $settings['test_formatter_setting_multiple'] . '|' . implode('|', $array)); break; } diff --git a/modules/field/tests/field_test.info b/modules/field/tests/field_test.info index 5a4086942dbf5cbe9adb8951807a4a2eae2c0f37..6385c992eef58da9a2b2a012b16462c6f365f478 100644 --- a/modules/field/tests/field_test.info +++ b/modules/field/tests/field_test.info @@ -6,8 +6,8 @@ files[] = field_test.entity.inc version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field/tests/field_test.module b/modules/field/tests/field_test.module index 7f43fbf09a07559199e3c639a0ee00a46586d205..0015cd90537b13955183003e9dc0499e36dceced 100644 --- a/modules/field/tests/field_test.module +++ b/modules/field/tests/field_test.module @@ -182,30 +182,6 @@ function field_test_field_create_field($field) { field_test_memorize(__FUNCTION__, $args); } -/** - * Memorize calls to hook_field_insert(). - */ -function field_test_field_insert($entity_type, $entity, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_update(). - */ -function field_test_field_update($entity_type, $entity, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - -/** - * Memorize calls to hook_field_delete(). - */ -function field_test_field_delete($entity_type, $entity, $field, $instance, $items) { - $args = func_get_args(); - field_test_memorize(__FUNCTION__, $args); -} - /** * Implements hook_entity_query_alter(). */ @@ -248,3 +224,38 @@ function field_test_field_attach_view_alter(&$output, $context) { $output['test_field'][] = array('#markup' => 'field_test_field_attach_view_alter'); } } + +/** + * Implements hook_field_widget_properties_alter(). + */ +function field_test_field_widget_properties_alter(&$widget, $context) { + // Make the alter_test_text field 42 characters for nodes and comments. + if (in_array($context['entity_type'], array('node', 'comment')) && ($context['field']['field_name'] == 'alter_test_text')) { + $widget['settings']['size'] = 42; + } +} + +/** + * Implements hook_field_widget_properties_ENTITY_TYPE_alter(). + */ +function field_test_field_widget_properties_user_alter(&$widget, $context) { + // Always use buttons for the alter_test_options field on user forms. + if ($context['field']['field_name'] == 'alter_test_options') { + $widget['type'] = 'options_buttons'; + } +} + +/** + * Implements hook_field_widget_form_alter(). + */ +function field_test_field_widget_form_alter(&$element, &$form_state, $context) { + switch ($context['field']['field_name']) { + case 'alter_test_text': + drupal_set_message('Field size: ' . $context['instance']['widget']['settings']['size']); + break; + + case 'alter_test_options': + drupal_set_message('Widget type: ' . $context['instance']['widget']['type']); + break; + } +} diff --git a/modules/field_ui/field_ui-rtl.css b/modules/field_ui/field_ui-rtl.css index 123a840bf8f3a1543cf1329dbfde6e476309a88b..1066baa2fbd2d9a56c2ebe1f53a21d5c5afeb5fe 100644 --- a/modules/field_ui/field_ui-rtl.css +++ b/modules/field_ui/field_ui-rtl.css @@ -1,3 +1,7 @@ +/** + * @file + * Right-to-left specific stylesheet for the Field UI module. + */ /* 'Manage fields' overview */ table.field-ui-overview tr.add-new .label-input { diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc index fa19afc0aecdb97d58375a45a103fecb7259a125..36c58f60cf59601ec27f9e910113832e904f0337 100644 --- a/modules/field_ui/field_ui.admin.inc +++ b/modules/field_ui/field_ui.admin.inc @@ -51,7 +51,7 @@ function field_ui_fields_list() { } /** - * Helper function to display a message about inactive fields. + * Displays a message listing the inactive fields of a given bundle. */ function field_ui_inactive_message($entity_type, $bundle) { $inactive_instances = field_ui_inactive_instances($entity_type, $bundle); @@ -72,9 +72,9 @@ function field_ui_inactive_message($entity_type, $bundle) { } /** - * Helper function: determines the rendering order of a tree array. + * Determines the rendering order of an array representing a tree. * - * This is intended as a callback for array_reduce(). + * Callback for array_reduce() within field_ui_table_pre_render(). */ function _field_ui_reduce_order($array, $a) { $array = !isset($array) ? array() : $array; @@ -91,8 +91,9 @@ function _field_ui_reduce_order($array, $a) { /** * Returns the region to which a row in the 'Manage fields' screen belongs. * - * This function is used as a #row_callback in field_ui_field_overview_form(), - * and is called during field_ui_table_pre_render(). + * This function is used as a #region_callback in + * field_ui_field_overview_form(). It is called during + * field_ui_table_pre_render(). */ function field_ui_field_overview_row_region($row) { switch ($row['#row_type']) { @@ -109,8 +110,9 @@ function field_ui_field_overview_row_region($row) { /** * Returns the region to which a row in the 'Manage display' screen belongs. * - * This function is used as a #row_callback in field_ui_field_overview_form(), - * and is called during field_ui_table_pre_render(). + * This function is used as a #region_callback in + * field_ui_field_overview_form(), and is called during + * field_ui_table_pre_render(). */ function field_ui_display_overview_row_region($row) { switch ($row['#row_type']) { @@ -215,9 +217,9 @@ function theme_field_ui_table($variables) { // Determine the colspan to use for region rows, by checking the number of // columns in the headers. - $colums_count = 0; + $columns_count = 0; foreach ($table['header'] as $header) { - $colums_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1); + $columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1); } // Render rows, region by region. @@ -230,7 +232,7 @@ function theme_field_ui_table($variables) { 'class' => array('region-title', 'region-' . $region_name_class . '-title'), 'no_striping' => TRUE, 'data' => array( - array('data' => $region['title'], 'colspan' => $colums_count), + array('data' => $region['title'], 'colspan' => $columns_count), ), ); } @@ -240,7 +242,7 @@ function theme_field_ui_table($variables) { 'class' => array('region-message', 'region-' . $region_name_class . '-message', $class), 'no_striping' => TRUE, 'data' => array( - array('data' => $region['message'], 'colspan' => $colums_count), + array('data' => $region['message'], 'colspan' => $columns_count), ), ); } @@ -274,9 +276,13 @@ function theme_field_ui_table($variables) { } /** - * Menu callback; listing of fields for a bundle. + * Form constructor for the 'Manage fields' form of a bundle. * * Allows fields and pseudo-fields to be re-ordered. + * + * @see field_ui_overview_form_validate(). + * @see field_ui_overview_form_submit(). + * @ingroup forms */ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle) { $bundle = field_extract_bundle($entity_type, $bundle); @@ -630,7 +636,9 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle } /** - * Validate handler for the field overview form. + * Form validation handler for field_ui_field_overview_form(). + * + * @see field_ui_field_overview_form_submit() */ function field_ui_field_overview_form_validate($form, &$form_state) { _field_ui_field_overview_form_validate_add_new($form, $form_state); @@ -638,9 +646,9 @@ function field_ui_field_overview_form_validate($form, &$form_state) { } /** - * Helper function for field_ui_field_overview_form_validate. + * Validates the 'add new field' row of field_ui_field_overview_form(). * - * Validate the 'add new field' row. + * @see field_ui_field_overview_form_validate() */ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) { $field = $form_state['values']['fields']['_add_new_field']; @@ -702,9 +710,9 @@ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) { } /** - * Helper function for field_ui_field_overview_form_validate. + * Validates the 'add existing field' row of field_ui_field_overview_form(). * - * Validate the 'add existing field' row. + * @see field_ui_field_overview_form_validate() */ function _field_ui_field_overview_form_validate_add_existing($form, &$form_state) { // The form element might be absent if no existing fields can be added to @@ -740,7 +748,9 @@ function _field_ui_field_overview_form_validate_add_existing($form, &$form_state } /** - * Submit handler for the field overview form. + * Form submission handler for field_ui_field_overview_form(). + * + * @see field_ui_field_overview_form_validate() */ function field_ui_field_overview_form_submit($form, &$form_state) { $form_values = $form_state['values']['fields']; @@ -846,7 +856,11 @@ function field_ui_field_overview_form_submit($form, &$form_state) { } /** - * Menu callback; presents field display settings for a given view mode. + * Form constructor for the field display settings for a given view mode. + * + * @see field_ui_display_overview_multistep_submit() + * @see field_ui_display_overview_form_submit() + * @ingroup forms */ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bundle, $view_mode) { $bundle = field_extract_bundle($entity_type, $bundle); @@ -1198,7 +1212,7 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund /** - * Form submit handler for multistep buttons on the 'Manage display' screen. + * Form submission handler for buttons in field_ui_display_overview_form(). */ function field_ui_display_overview_multistep_submit($form, &$form_state) { $trigger = $form_state['triggering_element']; @@ -1244,7 +1258,7 @@ function field_ui_display_overview_multistep_js($form, &$form_state) { $trigger = $form_state['triggering_element']; $op = $trigger['#op']; - // Pick the elements that need ro receive the ajax-new-content effect. + // Pick the elements that need to receive the ajax-new-content effect. switch ($op) { case 'edit': $updated_rows = array($trigger['#field_name']); @@ -1276,7 +1290,7 @@ function field_ui_display_overview_multistep_js($form, &$form_state) { } /** - * Submit handler for the display overview form. + * Form submission handler for field_ui_display_overview_form(). */ function field_ui_display_overview_form_submit($form, &$form_state) { $form_values = $form_state['values']; @@ -1354,7 +1368,7 @@ function field_ui_display_overview_form_submit($form, &$form_state) { } /** - * Helper function for field_ui_display_overview_form_submit(). + * Populates display settings for a new view mode from the default view mode. * * When an administrator decides to use custom display settings for a view mode, * that view mode needs to be initialized with the display settings for the @@ -1363,8 +1377,6 @@ function field_ui_display_overview_form_submit($form, &$form_state) { * them. It also modifies the passed-in $settings array, which the caller can * then save using field_bundle_settings(). * - * @see field_bundle_settings() - * * @param $entity_type * The bundle's entity type. * @param $bundle @@ -1374,6 +1386,9 @@ function field_ui_display_overview_form_submit($form, &$form_state) { * @param $settings * An associative array of bundle settings, as expected by * field_bundle_settings(). + * + * @see field_ui_display_overview_form_submit(). + * @see field_bundle_settings() */ function _field_ui_add_default_view_mode_settings($entity_type, $bundle, $view_mode, &$settings) { // Update display settings for field instances. @@ -1398,7 +1413,7 @@ function _field_ui_add_default_view_mode_settings($entity_type, $bundle, $view_m } /** - * Return an array of field_type options. + * Returns an array of field_type options. */ function field_ui_field_type_options() { $options = &drupal_static(__FUNCTION__); @@ -1420,7 +1435,7 @@ function field_ui_field_type_options() { } /** - * Return an array of widget type options for a field type. + * Returns an array of widget type options for a field type. * * If no field type is provided, returns a nested array of all widget types, * keyed by field type human name. @@ -1456,7 +1471,7 @@ function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) { } /** - * Return an array of formatter options for a field type. + * Returns an array of formatter options for a field type. * * If no field type is provided, returns a nested array of all formatters, keyed * by field type. @@ -1484,7 +1499,7 @@ function field_ui_formatter_options($field_type = NULL) { } /** - * Return an array of existing field to be added to a bundle. + * Returns an array of existing fields to be added to a bundle. */ function field_ui_existing_field_options($entity_type, $bundle) { $options = array(); @@ -1522,7 +1537,10 @@ function field_ui_existing_field_options($entity_type, $bundle) { } /** - * Menu callback; presents the field settings edit page. + * Form constructor for the field settings edit page. + * + * @see field_ui_settings_form_submit() + * @ingroups forms */ function field_ui_field_settings_form($form, &$form_state, $instance) { $bundle = $instance['bundle']; @@ -1576,7 +1594,7 @@ function field_ui_field_settings_form($form, &$form_state, $instance) { } /** - * Save a field's settings after editing. + * Form submission handler for field_ui_field_settings_form(). */ function field_ui_field_settings_form_submit($form, &$form_state) { $form_values = $form_state['values']; @@ -1603,7 +1621,15 @@ function field_ui_field_settings_form_submit($form, &$form_state) { } /** - * Menu callback; select a widget for the field. + * Form constructor for the widget selection form. + * + * Path: BUNDLE_ADMIN_PATH/fields/%field/widget-type, where BUNDLE_ADMIN_PATH is + * the path stored in the ['admin']['info'] property in the return value of + * hook_entity_info(). + * + * @see field_ui_menu() + * @see field_ui_widget_type_form_submit() + * @ingroup forms */ function field_ui_widget_type_form($form, &$form_state, $instance) { drupal_set_title($instance['label']); @@ -1647,7 +1673,7 @@ function field_ui_widget_type_form($form, &$form_state, $instance) { } /** - * Submit the change in widget type. + * Form submission handler for field_ui_widget_type_form(). */ function field_ui_widget_type_form_submit($form, &$form_state) { $form_values = $form_state['values']; @@ -1677,7 +1703,10 @@ function field_ui_widget_type_form_submit($form, &$form_state) { } /** - * Menu callback; present a form for removing a field instance from a bundle. + * Form constructor for removing a field instance from a bundle. + * + * @see field_ui_delete_form_submit() + * @ingroup forms */ function field_ui_field_delete_form($form, &$form_state, $instance) { $bundle = $instance['bundle']; @@ -1707,9 +1736,10 @@ function field_ui_field_delete_form($form, &$form_state, $instance) { } /** - * Removes a field instance from a bundle. + * Form submission handler for field_ui_field_delete_form(). * - * If the field has no more instances, it will be marked as deleted too. + * Removes a field instance from a bundle. If the field has no more instances, + * it will be marked as deleted too. */ function field_ui_field_delete_form_submit($form, &$form_state) { $form_values = $form_state['values']; @@ -1743,7 +1773,11 @@ function field_ui_field_delete_form_submit($form, &$form_state) { } /** - * Menu callback; presents the field instance edit page. + * Form constructor for the field instance settings form. + * + * @see field_ui_field_edit_form_validate() + * @see field_ui_field_edit_form_submit() + * @ingroup forms */ function field_ui_field_edit_form($form, &$form_state, $instance) { $bundle = $instance['bundle']; @@ -1924,7 +1958,7 @@ function field_ui_field_edit_instance_pre_render($element) { } /** - * Build default value fieldset. + * Builds the default value fieldset for a given field instance. */ function field_ui_default_value_widget($field, $instance, &$form, &$form_state) { $field_name = $field['field_name']; @@ -1947,13 +1981,15 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state) $instance['description'] = ''; // @todo Allow multiple values (requires more work on 'add more' JS handler). - $element += field_default_form(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state, 0); + $element += field_default_form($instance['entity_type'], NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state, 0); return $element; } /** - * Form validation handler for field instance settings form. + * Form validation handler for field_ui_field_edit_form(). + * + * @see field_ui_field_edit_form_submit(). */ function field_ui_field_edit_form_validate($form, &$form_state) { // Take the incoming values as the $instance definition, so that the 'default @@ -1988,7 +2024,9 @@ function field_ui_field_edit_form_validate($form, &$form_state) { } /** - * Form submit handler for field instance settings form. + * Form submission handler for field_ui_field_edit_form(). + * + * @see field_ui_field_edit_form_validate(). */ function field_ui_field_edit_form_submit($form, &$form_state) { $instance = $form_state['values']['instance']; @@ -2028,7 +2066,9 @@ function field_ui_field_edit_form_submit($form, &$form_state) { } /** - * Helper functions to handle multipage redirects. + * Extracts next redirect path from an array of multiple destinations. + * + * @see field_ui_next_destination() */ function field_ui_get_destinations($destinations) { $path = array_shift($destinations); @@ -2040,7 +2080,7 @@ function field_ui_get_destinations($destinations) { } /** - * Return the next redirect path in a multipage sequence. + * Returns the next redirect path in a multipage sequence. */ function field_ui_next_destination($entity_type, $bundle) { $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array(); diff --git a/modules/field_ui/field_ui.api.php b/modules/field_ui/field_ui.api.php index 2340125122b2551e59119c36308c27ed4caead5e..f903e12f378ec2958bf05dcf3097b5567a5584c8 100644 --- a/modules/field_ui/field_ui.api.php +++ b/modules/field_ui/field_ui.api.php @@ -132,7 +132,7 @@ function hook_field_widget_settings_form($field, $instance) { /** - * Returns form elements for a formatter's settings. + * Specify the form elements for a formatter's settings. * * @param $field * The field structure being configured. @@ -170,7 +170,7 @@ function hook_field_formatter_settings_form($field, $instance, $view_mode, $form } /** - * Returns a short summary for the current formatter settings of an instance. + * Return a short summary for the current formatter settings of an instance. * * If an empty result is returned, the formatter is assumed to have no * configurable settings, and no UI will be provided to display a settings diff --git a/modules/field_ui/field_ui.css b/modules/field_ui/field_ui.css index c25d2a436c8dedf4d54469edb524b72d5fd1cc76..ae469e2075dbc202525e5d9ed5402e436cd4faf6 100644 --- a/modules/field_ui/field_ui.css +++ b/modules/field_ui/field_ui.css @@ -1,4 +1,8 @@ - +/** + * @file + * Stylesheet for the Field UI module. + */ + /* 'Manage fields' and 'Manage display' overviews */ table.field-ui-overview tr.add-new .label-input { float: left; /* LTR */ diff --git a/modules/field_ui/field_ui.info b/modules/field_ui/field_ui.info index 3212c2ae284ac7c9ded91ebecc510c50871304d8..14165d36c12a4cc0588d5019185e9e96cac44d2b 100644 --- a/modules/field_ui/field_ui.info +++ b/modules/field_ui/field_ui.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = field_ui.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/field_ui/field_ui.js b/modules/field_ui/field_ui.js index b63399f3d28dab70a58958f9bc87a1cdbfdc1c87..0a41c4c6ffc9986f44e670bb65b68d8cd683b17a 100644 --- a/modules/field_ui/field_ui.js +++ b/modules/field_ui/field_ui.js @@ -1,4 +1,8 @@ - +/** + * @file + * Attaches the behaviors for the Field UI module. + */ + (function($) { Drupal.behaviors.fieldUIFieldOverview = { @@ -118,7 +122,7 @@ Drupal.fieldUIOverview = { data.tableDrag = tableDrag; // Create the row handler, make it accessible from the DOM row element. - var rowHandler = eval('new rowHandlers.' + data.rowHandler + '(row, data);'); + var rowHandler = new rowHandlers[data.rowHandler](row, data); $(row).data('fieldUIRowHandler', rowHandler); } }); diff --git a/modules/field_ui/field_ui.module b/modules/field_ui/field_ui.module index 3be23c88953c1f2ffecf0b4e94859e22191d144c..7bfb4faa7875757881924f331dcc177de0080251 100644 --- a/modules/field_ui/field_ui.module +++ b/modules/field_ui/field_ui.module @@ -1,8 +1,7 @@ <?php - /** * @file - * Allows administrators to associate custom fields to fieldable types. + * Allows administrators to attach custom fields to fieldable types. */ /** @@ -213,6 +212,11 @@ function field_ui_menu() { * The position of $bundle_name in $map. * @param $map * The translated menu router path argument map. + * + * @return + * The field instance array. + * + * @ingroup field */ function field_ui_menu_load($field_name, $entity_type, $bundle_name, $bundle_pos, $map) { // Extract the actual bundle name from the translated argument map. @@ -261,7 +265,8 @@ function _field_ui_view_mode_menu_access($entity_type, $bundle, $view_mode, $acc // part of _menu_check_access(). if ($visibility) { // Grab the variable 'access arguments' part. - $args = array_slice(func_get_args(), 4); + $all_args = func_get_args(); + $args = array_slice($all_args, 4); $callback = empty($access_callback) ? 0 : trim($access_callback); if (is_numeric($callback)) { return (bool) $callback; @@ -313,7 +318,7 @@ function field_ui_field_attach_create_bundle($entity_type, $bundle) { } /** - * Helper function to create the right administration path for a bundle. + * Determines the adminstration path for a bundle. */ function _field_ui_bundle_admin_path($entity_type, $bundle_name) { $bundles = field_info_bundles($entity_type); @@ -324,7 +329,7 @@ function _field_ui_bundle_admin_path($entity_type, $bundle_name) { } /** - * Helper function to identify inactive fields within a bundle. + * Identifies inactive fields within a bundle. */ function field_ui_inactive_instances($entity_type, $bundle_name = NULL) { if (!empty($bundle_name)) { @@ -351,7 +356,12 @@ function field_ui_inactive_instances($entity_type, $bundle_name = NULL) { } /** - * Add a button Save and add fields to Create content type form. + * Implements hook_form_FORM_ID_alter(). + * + * Adds a button 'Save and add fields' to the 'Create content type' form. + * + * @see node_type_form() + * @see field_ui_form_node_type_form_submit() */ function field_ui_form_node_type_form_alter(&$form, $form_state) { // We want to display the button only on add page. @@ -366,7 +376,9 @@ function field_ui_form_node_type_form_alter(&$form, $form_state) { } /** - * Redirect to manage fields form. + * Form submission handler for the 'Save and add fields' button. + * + * @see field_ui_form_node_type_form_alter() */ function field_ui_form_node_type_form_submit($form, &$form_state) { if ($form_state['triggering_element']['#parents'][0] === 'save_continue') { diff --git a/modules/field_ui/field_ui.test b/modules/field_ui/field_ui.test index e7c5e18e796bdbdb7bd8ad7edbc5ee3c3882fade..f1e770baa6666103c1e1cceee3054b26b7095665 100644 --- a/modules/field_ui/field_ui.test +++ b/modules/field_ui/field_ui.test @@ -6,7 +6,7 @@ */ /** - * Helper class for Field UI test classes. + * Provides common functionality for the Field UI test classes. */ class FieldUITestCase extends DrupalWebTestCase { @@ -34,10 +34,10 @@ class FieldUITestCase extends DrupalWebTestCase { } /** - * Create a new field through the Field UI. + * Creates a new field through the Field UI. * * @param $bundle_path - * Path of the 'Manage fields' page for the bundle. + * Admin path of the bundle that the new field is to be attached to. * @param $initial_edit * $edit parameter for drupalPost() on the first step ('Manage fields' * screen). @@ -74,10 +74,10 @@ class FieldUITestCase extends DrupalWebTestCase { } /** - * Add an existing field through the Field UI. + * Adds an existing field through the Field UI. * * @param $bundle_path - * Path of the 'Manage fields' page for the bundle. + * Admin path of the bundle that the field is to be attached to. * @param $initial_edit * $edit parameter for drupalPost() on the first step ('Manage fields' * screen). @@ -105,10 +105,10 @@ class FieldUITestCase extends DrupalWebTestCase { } /** - * Delete a field instance through the Field UI. + * Deletes a field instance through the Field UI. * * @param $bundle_path - * Path of the 'Manage fields' page for the bundle. + * Admin path of the bundle that the field instance is to be deleted from. * @param $field_name * The name of the field. * @param $label @@ -131,7 +131,7 @@ class FieldUITestCase extends DrupalWebTestCase { } /** - * Field UI tests for the 'Manage fields' screen. + * Tests the functionality of the 'Manage fields' screen. */ class FieldUIManageFieldsTestCase extends FieldUITestCase { public static function getInfo() { @@ -152,7 +152,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Main entry point for the field CRUD tests. + * Runs the field CRUD tests. * * In order to act on the same fields, and not create the fields over and over * again the following tests create, update and delete the same fields. @@ -165,7 +165,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Test the manage fields page. + * Tests the manage fields page. */ function manageFieldsPage() { $this->drupalGet('admin/structure/types/manage/' . $this->hyphen_type . '/fields'); @@ -190,7 +190,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Test adding a new field. + * Tests adding a new field. * * @todo Assert properties can bet set in the form and read back in $field and * $instances. @@ -212,7 +212,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Test editing an existing field. + * Tests editing an existing field. */ function updateField() { // Go to the field edit page. @@ -235,7 +235,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Test adding an existing field in another content type. + * Tests adding an existing field in another content type. */ function addExistingField() { // Check "Add existing field" appears. @@ -256,7 +256,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Assert the field settings. + * Asserts field settings are as expected. * * @param $bundle * The bundle name for the instance. @@ -378,7 +378,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Test that Field UI respects the 'no_ui' option in hook_field_info(). + * Tests that Field UI respects the 'no_ui' option in hook_field_info(). */ function testHiddenFields() { $bundle_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/'; @@ -429,7 +429,7 @@ class FieldUIManageFieldsTestCase extends FieldUITestCase { } /** - * Field UI tests for the 'Manage display' screens. + * Tests the functionality of the 'Manage display' screens. */ class FieldUIManageDisplayTestCase extends FieldUITestCase { public static function getInfo() { @@ -445,7 +445,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase { } /** - * Test formatter formatter settings. + * Tests formatter settings. */ function testFormatterUI() { $manage_fields = 'admin/structure/types/manage/' . $this->hyphen_type; @@ -493,7 +493,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase { } /** - * Test switching view modes to use custom or 'default' settings'. + * Tests switching view modes to use custom or 'default' settings'. */ function testViewModeCustom() { // Create a field, and a node with some data for the field. @@ -565,7 +565,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase { } /** - * Pass if the text is found in the rendered node in a given view mode. + * Asserts that a string is found in the rendered node in a view mode. * * @param $node * The node. @@ -584,7 +584,7 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase { } /** - * Pass if the text is node found in the rendered node in a given view mode. + * Asserts that a string is not found in the rendered node in a view mode. * * @param $node * The node. @@ -602,7 +602,10 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase { } /** - * Helper for assertNodeViewText and assertNodeViewNoText. + * Asserts that a string is (not) found in the rendered nodein a view mode. + * + * This helper function is used by assertNodeViewText() and + * assertNodeViewNoText(). * * @param $node * The node. @@ -643,3 +646,87 @@ class FieldUIManageDisplayTestCase extends FieldUITestCase { return $return; } } + +/** + * Tests custom widget hooks and callbacks on the field administration pages. + */ +class FieldUIAlterTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Widget customization', + 'description' => 'Test custom field widget hooks and callbacks on field administration pages.', + 'group' => 'Field UI', + ); + } + + function setUp() { + parent::setUp(array('field_test')); + + // Create test user. + $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer users')); + $this->drupalLogin($admin_user); + } + + /** + * Tests hook_field_widget_properties_alter() on the default field widget. + * + * @see field_test_field_widget_properties_alter() + * @see field_test_field_widget_properties_user_alter() + * @see field_test_field_widget_form_alter() + */ + function testDefaultWidgetPropertiesAlter() { + // Create the alter_test_text field and an instance on article nodes. + field_create_field(array( + 'field_name' => 'alter_test_text', + 'type' => 'text', + )); + field_create_instance(array( + 'field_name' => 'alter_test_text', + 'entity_type' => 'node', + 'bundle' => 'article', + 'widget' => array( + 'type' => 'text_textfield', + 'size' => 60, + ), + )); + + // Test that field_test_field_widget_properties_alter() sets the size to + // 42 and that field_test_field_widget_form_alter() reports the correct + // size when the form is displayed. + $this->drupalGet('admin/structure/types/manage/article/fields/alter_test_text'); + $this->assertText('Field size: 42', 'Altered field size is found in hook_field_widget_form_alter().'); + + // Create the alter_test_options field. + field_create_field(array( + 'field_name' => 'alter_test_options', + 'type' => 'list_text' + )); + // Create instances on users and page nodes. + field_create_instance(array( + 'field_name' => 'alter_test_options', + 'entity_type' => 'user', + 'bundle' => 'user', + 'widget' => array( + 'type' => 'options_select', + ) + )); + field_create_instance(array( + 'field_name' => 'alter_test_options', + 'entity_type' => 'node', + 'bundle' => 'page', + 'widget' => array( + 'type' => 'options_select', + ) + )); + + // Test that field_test_field_widget_properties_user_alter() replaces + // the widget and that field_test_field_widget_form_alter() reports the + // correct widget name when the form is displayed. + $this->drupalGet('admin/config/people/accounts/fields/alter_test_options'); + $this->assertText('Widget type: options_buttons', 'Widget type is altered for users in hook_field_widget_form_alter().'); + + // Test that the widget is not altered on page nodes. + $this->drupalGet('admin/structure/types/manage/page/fields/alter_test_options'); + $this->assertText('Widget type: options_select', 'Widget type is not altered for pages in hook_field_widget_form_alter().'); + } +} diff --git a/modules/file/file.api.php b/modules/file/file.api.php index 76fb9861070a9904eecd93bc9b698be958e6e8b9..72aae40c9b105acbe6b080e02cfb20238225d47e 100644 --- a/modules/file/file.api.php +++ b/modules/file/file.api.php @@ -12,8 +12,8 @@ * file is referenced, e.g., only users with access to a node should be allowed * to download files attached to that node. * - * @param $field - * The field to which the file belongs. + * @param array $file_item + * The array of information about the file to check access for. * @param $entity_type * The type of $entity; for example, 'node' or 'user'. * @param $entity @@ -26,7 +26,7 @@ * * @see hook_field_access(). */ -function hook_file_download_access($field, $entity_type, $entity) { +function hook_file_download_access($file_item, $entity_type, $entity) { if ($entity_type == 'node') { return node_access('view', $entity); } @@ -45,8 +45,8 @@ function hook_file_download_access($field, $entity_type, $entity) { * An array of grants gathered by hook_file_download_access(). The array is * keyed by the module that defines the entity type's access control; the * values are Boolean grant responses for each module. - * @param $field - * The field to which the file belongs. + * @param array $file_item + * The array of information about the file to alter access for. * @param $entity_type * The type of $entity; for example, 'node' or 'user'. * @param $entity @@ -55,10 +55,10 @@ function hook_file_download_access($field, $entity_type, $entity) { * @return * An array of grants, keyed by module name, each with a Boolean grant value. * Return an empty array to assert FALSE. You may choose to return your own - * module's value in addition to other grants or to overwrite the values set by - * other modules. + * module's value in addition to other grants or to overwrite the values set + * by other modules. */ -function hook_file_download_access_alter(&$grants, $field, $entity_type, $entity) { +function hook_file_download_access_alter(&$grants, $file_item, $entity_type, $entity) { // For our example module, we always enforce the rules set by node module. if (isset($grants['node'])) { $grants = array('node' => $grants['node']); diff --git a/modules/file/file.css b/modules/file/file.css index 40451b8caa26db69c68c1b0c65c5a684e628b4f3..bd4a05970ec4ffcef64eb32e9affdfd08ae4e7a8 100644 --- a/modules/file/file.css +++ b/modules/file/file.css @@ -1,3 +1,7 @@ +/** + * @file + * Admin stylesheet for file module. + */ /** * Managed file element styles. diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index 7f5906ece4c11da311d7b4504f9a4f32fec785d5..7a5697ccb24580335c9167da680598da83ae651a 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -312,7 +312,7 @@ function file_field_delete_revision($entity_type, $entity, $field, $instance, $l } /** - * Decrements a file usage count and attempts to delete it. + * Decrements the usage count for a file and attempts to delete it. * * This function only has an effect if the file being deleted is used only by * File module. @@ -358,12 +358,13 @@ function file_field_is_empty($item, $field) { } /** - * Determine whether a file should be displayed when outputting field content. + * Determines whether a file should be displayed when outputting field content. * * @param $item * A field item array. * @param $field * A field array. + * * @return * Boolean TRUE if the file will be displayed, FALSE if the file is hidden. */ @@ -516,10 +517,11 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco } /** - * Get the upload validators for a file field. + * Retrieves the upload validators for a file field. * * @param $field * A field array. + * * @return * An array suitable for passing to file_save_upload() or the file field * element's '#upload_validators' property. @@ -545,7 +547,7 @@ function file_field_widget_upload_validators($field, $instance) { } /** - * Determine the URI for a file field instance. + * Determines the URI for a file field instance. * * @param $field * A field array. @@ -553,6 +555,7 @@ function file_field_widget_upload_validators($field, $instance) { * A field instance array. * @param $data * An array of token objects to pass to token_replace(). + * * @return * A file directory URI with tokens replaced. * @@ -704,10 +707,13 @@ function file_field_widget_process_multiple($element, &$form_state, $form) { } /** - * Helper function for file_field_widget_process_multiple(). + * Retrieves the file description from a field field element. + * + * This helper function is used by file_field_widget_process_multiple(). * * @param $element * The element being processed. + * * @return * A description of the file suitable for use in the administrative interface. */ @@ -725,7 +731,7 @@ function _file_field_get_description_from_element($element) { } /** - * Submit handler for upload and remove buttons of file_generic fields. + * Form submission handler for upload/remove button of file_field_widget_form(). * * This runs in addition to and after file_managed_file_submit(). * @@ -970,11 +976,13 @@ function file_field_formatter_view($entity_type, $entity, $field, $instance, $la break; case 'file_table': - // Display all values in a single element.. - $element[0] = array( - '#theme' => 'file_formatter_table', - '#items' => $items, - ); + if (!empty($items)) { + // Display all values in a single element.. + $element[0] = array( + '#theme' => 'file_formatter_table', + '#items' => $items, + ); + } break; } diff --git a/modules/file/file.info b/modules/file/file.info index 12cb795d8483c5106eb6aa1792f031823a128ac7..5fd07dd9d10b5e7139abc57002d1835e80369af5 100644 --- a/modules/file/file.info +++ b/modules/file/file.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = field files[] = tests/file.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/file/file.module b/modules/file/file.module index f979a69fb9c6877372bd214dba5cc98c9c77273e..506b0e91dc26f49b38befb220fc22b30244deb8b 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -56,7 +56,7 @@ function file_menu() { /** * Implements hook_element_info(). * - * The managed file element may be used independently anywhere in Drupal. + * The managed file element may be used anywhere in Drupal. */ function file_element_info() { $file_path = drupal_get_path('module', 'file'); @@ -164,24 +164,27 @@ function file_file_download($uri, $field_type = 'file') { // Try to load $entity and $field. $entity = entity_load($entity_type, array($id)); $entity = reset($entity); - $field = NULL; + $field = field_info_field($field_name); + + // Load the field item that references the file. + $field_item = NULL; if ($entity) { - // Load all fields for that entity. + // Load all field items for that entity. $field_items = field_get_items($entity_type, $entity, $field_name); // Find the field item with the matching URI. - foreach ($field_items as $field_item) { - if ($field_item['uri'] == $uri) { - $field = $field_item; + foreach ($field_items as $item) { + if ($item['uri'] == $uri) { + $field_item = $item; break; } } } - // Check that $entity and $field were loaded successfully and check if - // access to that field is not disallowed. If any of these checks fail, - // stop checking access for this reference. - if (empty($entity) || empty($field) || !field_access('view', $field, $entity_type, $entity)) { + // Check that $entity, $field and $field_item were loaded successfully + // and check if access to that field is not disallowed. If any of these + // checks fail, stop checking access for this reference. + if (empty($entity) || empty($field) || empty($field_item) || !field_access('view', $field, $entity_type, $entity)) { $denied = TRUE; break; } @@ -190,10 +193,10 @@ function file_file_download($uri, $field_type = 'file') { // Default to FALSE and let entities overrule this ruling. $grants = array('system' => FALSE); foreach (module_implements('file_download_access') as $module) { - $grants = array_merge($grants, array($module => module_invoke($module, 'file_download_access', $field, $entity_type, $entity))); + $grants = array_merge($grants, array($module => module_invoke($module, 'file_download_access', $field_item, $entity_type, $entity))); } // Allow other modules to alter the returned grants/denies. - drupal_alter('file_download_access', $grants, $field, $entity_type, $entity); + drupal_alter('file_download_access', $grants, $field_item, $entity_type, $entity); if (in_array(TRUE, $grants)) { // If TRUE is returned, access is granted and no further checks are @@ -317,7 +320,7 @@ function file_ajax_progress($key) { } /** - * Determine the preferred upload progress implementation. + * Determines the preferred upload progress implementation. * * @return * A string indicating which upload progress system is available. Either "apc" @@ -570,7 +573,9 @@ function file_managed_file_validate(&$element, &$form_state) { } /** - * Submit handler for upload and remove buttons of managed_file elements. + * Form submission handler for upload / remove buttons of managed_file elements. + * + * @see file_managed_file_process() */ function file_managed_file_submit($form, &$form_state) { // Determine whether it was the upload or the remove button that was clicked, @@ -611,10 +616,11 @@ function file_managed_file_submit($form, &$form_state) { } /** - * Given a managed_file element, save any files that have been uploaded into it. + * Saves any files that have been uploaded into a managed_file element. * * @param $element * The FAPI element whose values are being saved. + * * @return * The file object representing the file that was saved, or FALSE if no file * was saved. @@ -761,13 +767,14 @@ function theme_file_icon($variables) { } /** - * Given a file object, create a URL to a matching icon. + * Creates a URL to the icon for a file object. * * @param $file * A file object. * @param $icon_directory * (optional) A path to a directory of icons to be used for files. Defaults to * the value of the "file_icon_directory" variable. + * * @return * A URL string to the icon, or FALSE if an appropriate icon cannot be found. */ @@ -779,13 +786,14 @@ function file_icon_url($file, $icon_directory = NULL) { } /** - * Given a file object, create a path to a matching icon. + * Creates a path to the icon for a file object. * * @param $file * A file object. * @param $icon_directory * (optional) A path to a directory of icons to be used for files. Defaults to * the value of the "file_icon_directory" variable. + * * @return * A string to the icon as a local path, or FALSE if an appropriate icon could * not be found. @@ -831,10 +839,11 @@ function file_icon_path($file, $icon_directory = NULL) { } /** - * Determine the generic icon MIME package based on a file's MIME type. + * Determines the generic icon MIME package based on a file's MIME type. * * @param $file * A file object. + * * @return * The generic icon MIME package expected for this file. */ @@ -962,7 +971,7 @@ function file_icon_map($file) { */ /** - * Gets a list of references to a file. + * Retrieves a list of references to a file. * * @param $file * A file object. diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index 59f6e0cb0bc841fe7297aa673da58cea354e3de5..1b5fdf5cd9f7b78b6942fd12806bd5c6aef06ea8 100644 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -6,7 +6,7 @@ */ /** - * This class provides methods specifically for testing File's field handling. + * Provides methods specifically for testing File module's field handling. */ class FileFieldTestCase extends DrupalWebTestCase { protected $admin_user; @@ -27,7 +27,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Get a sample file of the specified type. + * Retrieves a sample file of the specified type. */ function getTestFile($type_name, $size = NULL) { // Get a file to upload. @@ -40,14 +40,14 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Get the fid of the last inserted file. + * Retrieves the fid of the last inserted file. */ function getLastFileId() { return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField(); } /** - * Create a new file field. + * Creates a new file field. * * @param $name * The name of the new field (all lowercase), exclude the "field_" prefix. @@ -74,7 +74,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Attach a file field to an entity. + * Attaches a file field to an entity. * * @param $name * The name of the new field (all lowercase), exclude the "field_" prefix. @@ -108,7 +108,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Update an existing file field with new settings. + * Updates an existing file field with new settings. */ function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) { $instance = field_info_instance('node', $name, $type_name); @@ -119,7 +119,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Upload a file to a node. + * Uploads a file to a node. */ function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE, $extras = array()) { $langcode = LANGUAGE_NONE; @@ -150,7 +150,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Remove a file from a node. + * Removes a file from a node. * * Note that if replacing a file, it must first be removed then added again. */ @@ -164,7 +164,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Replace a file within a node. + * Replaces a file within a node. */ function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) { $edit = array( @@ -177,7 +177,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Assert that a file exists physically on disk. + * Asserts that a file exists physically on disk. */ function assertFileExists($file, $message = NULL) { $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri)); @@ -185,7 +185,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Assert that a file exists in the database. + * Asserts that a file exists in the database. */ function assertFileEntryExists($file, $message = NULL) { entity_get_controller('file')->resetCache(); @@ -195,7 +195,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Assert that a file does not exist on disk. + * Asserts that a file does not exist on disk. */ function assertFileNotExists($file, $message = NULL) { $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri)); @@ -203,7 +203,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Assert that a file does not exist in the database. + * Asserts that a file does not exist in the database. */ function assertFileEntryNotExists($file, $message) { entity_get_controller('file')->resetCache(); @@ -212,7 +212,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Assert that a file's status is set to permanent in the database. + * Asserts that a file's status is set to permanent in the database. */ function assertFileIsPermanent($file, $message = NULL) { $message = isset($message) ? $message : t('File %file is permanent.', array('%file' => $file->uri)); @@ -221,7 +221,7 @@ class FileFieldTestCase extends DrupalWebTestCase { } /** - * Test class for testing the 'managed_file' element type on its own, not as part of a file field. + * Tests the 'managed_file' element type. * * @todo Create a FileTestCase base class and move FileFieldTestCase methods * that aren't related to fields into it. @@ -311,7 +311,7 @@ class FileManagedFileElementTestCase extends FileFieldTestCase { } /** - * Test class to test file field widget, single and multi-valued, with and without Ajax, with public and private files. + * Tests file field widget. */ class FileFieldWidgetTestCase extends FileFieldTestCase { public static function getInfo() { @@ -323,7 +323,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { } /** - * Tests upload and remove buttons, with and without Ajax, for a single-valued File field. + * Tests upload and remove buttons for a single-valued File field. */ function testSingleValuedWidget() { // Use 'page' instead of 'article', so that the 'article' image field does @@ -380,7 +380,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { } /** - * Tests upload and remove buttons, with and without Ajax, for multiple multi-valued File field. + * Tests upload and remove buttons for multiple multi-valued File fields. */ function testMultiValuedWidget() { // Use 'page' instead of 'article', so that the 'article' image field does @@ -616,7 +616,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { } /** - * Test class to test file handling with node revisions. + * Tests file handling with node revisions. */ class FileFieldRevisionTestCase extends FileFieldTestCase { public static function getInfo() { @@ -628,7 +628,7 @@ class FileFieldRevisionTestCase extends FileFieldTestCase { } /** - * Test creating multiple revisions of a node and managing the attached files. + * Tests creating multiple revisions of a node and managing attached files. * * Expected behaviors: * - Adding a new revision will make another entry in the field table, but @@ -731,7 +731,7 @@ class FileFieldRevisionTestCase extends FileFieldTestCase { } /** - * Test class to check that formatters are working properly. + * Tests that formatters are working properly. */ class FileFieldDisplayTestCase extends FileFieldTestCase { public static function getInfo() { @@ -743,7 +743,7 @@ class FileFieldDisplayTestCase extends FileFieldTestCase { } /** - * Test normal formatter display on node display. + * Tests normal formatter display on node display. */ function testNodeDisplay() { $field_name = strtolower($this->randomName()); @@ -760,6 +760,19 @@ class FileFieldDisplayTestCase extends FileFieldTestCase { $field = field_info_field($field_name); $instance = field_info_instance('node', $field_name, $type_name); + // Create a new node *without* the file field set, and check that the field + // is not shown for each node display. + $node = $this->drupalCreateNode(array('type' => $type_name)); + $file_formatters = array('file_default', 'file_table', 'file_url_plain', 'hidden'); + foreach ($file_formatters as $formatter) { + $edit = array( + "fields[$field_name][type]" => $formatter, + ); + $this->drupalPost("admin/structure/types/manage/$type_name/display", $edit, t('Save')); + $this->drupalGet('node/' . $node->nid); + $this->assertNoText($field_name, t('Field label is hidden when no file attached for formatter %formatter', array('%formatter' => $formatter))); + } + $test_file = $this->getTestFile('text'); // Create a new node with the uploaded file. @@ -782,7 +795,7 @@ class FileFieldDisplayTestCase extends FileFieldTestCase { } /** - * Test class to check for various validations. + * Tests various validations. */ class FileFieldValidateTestCase extends FileFieldTestCase { protected $field; @@ -797,7 +810,7 @@ class FileFieldValidateTestCase extends FileFieldTestCase { } /** - * Test required property on file fields. + * Tests the required property on file fields. */ function testRequired() { $type_name = 'article'; @@ -845,7 +858,7 @@ class FileFieldValidateTestCase extends FileFieldTestCase { } /** - * Test the max file size validator. + * Tests the max file size validator. */ function testFileMaxSize() { $type_name = 'article'; @@ -897,7 +910,7 @@ class FileFieldValidateTestCase extends FileFieldTestCase { } /** - * Test the file extension, do additional checks if mimedetect is installed. + * Tests file extension checking. */ function testFileExtension() { $type_name = 'article'; @@ -943,7 +956,7 @@ class FileFieldValidateTestCase extends FileFieldTestCase { } /** - * Test class to check that files are uploaded to proper locations. + * Tests that files are uploaded to proper locations. */ class FileFieldPathTestCase extends FileFieldTestCase { public static function getInfo() { @@ -955,7 +968,7 @@ class FileFieldPathTestCase extends FileFieldTestCase { } /** - * Test normal formatter display on node display. + * Tests the normal formatter display on node display. */ function testUploadPath() { $field_name = strtolower($this->randomName()); @@ -1000,7 +1013,7 @@ class FileFieldPathTestCase extends FileFieldTestCase { } /** - * A loose assertion to check that a file is uploaded to the right location. + * Asserts that a file is uploaded to the right location. * * @param $expected_path * The location where the file is expected to be uploaded. Duplicate file @@ -1023,7 +1036,7 @@ class FileFieldPathTestCase extends FileFieldTestCase { } /** - * Test file token replacement in strings. + * Tests the file token replacement in strings. */ class FileTokenReplaceTestCase extends FileFieldTestCase { public static function getInfo() { @@ -1052,6 +1065,9 @@ class FileTokenReplaceTestCase extends FileFieldTestCase { $instance = field_info_instance('node', $field_name, $type_name); $test_file = $this->getTestFile('text'); + // Coping a file to test uploads with non-latin filenames. + $filename = drupal_dirname($test_file->uri) . '/текстовый файл.txt'; + $test_file = file_copy($test_file, $filename); // Create a new node with the uploaded file. $nid = $this->uploadNodeFile($test_file, $field_name, $type_name); @@ -1095,7 +1111,7 @@ class FileTokenReplaceTestCase extends FileFieldTestCase { } /** - * Test class to test file access on private nodes. + * Tests file access on private nodes. */ class FilePrivateTestCase extends FileFieldTestCase { public static function getInfo() { @@ -1107,13 +1123,13 @@ class FilePrivateTestCase extends FileFieldTestCase { } function setUp() { - parent::setUp('node_access_test'); + parent::setUp(array('node_access_test', 'field_test')); node_access_rebuild(); variable_set('node_access_test_private', TRUE); } /** - * Uploads a file to a private node, then tests that access is allowed and denied when appropriate. + * Tests file access for file uploaded to a private node. */ function testPrivateFile() { // Use 'page' instead of 'article', so that the 'article' image field does @@ -1124,6 +1140,10 @@ class FilePrivateTestCase extends FileFieldTestCase { $field_name = strtolower($this->randomName()); $this->createFileField($field_name, $type_name, array('uri_scheme' => 'private')); + // Create a field with no view access - see field_test_field_access(). + $no_access_field_name = 'field_no_view_access'; + $this->createFileField($no_access_field_name, $type_name, array('uri_scheme' => 'private')); + $test_file = $this->getTestFile('text'); $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE)); $node = node_load($nid, NULL, TRUE); @@ -1134,5 +1154,14 @@ class FilePrivateTestCase extends FileFieldTestCase { $this->drupalLogOut(); $this->drupalGet(file_create_url($node_file->uri)); $this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.')); + + // Test with the field that should deny access through field access. + $this->drupalLogin($this->admin_user); + $nid = $this->uploadNodeFile($test_file, $no_access_field_name, $type_name, TRUE, array('private' => TRUE)); + $node = node_load($nid, NULL, TRUE); + $node_file = (object) $node->{$no_access_field_name}[LANGUAGE_NONE][0]; + // Ensure the file cannot be downloaded. + $this->drupalGet(file_create_url($node_file->uri)); + $this->assertResponse(403, t('Confirmed that access is denied for the file without view field access permission.')); } } diff --git a/modules/file/tests/file_module_test.info b/modules/file/tests/file_module_test.info index cf503767222d98365eac885ffb0ba81a9c90ab30..5314f21af8e4ab1ab3066ac7acc13de0e7b30b7e 100644 --- a/modules/file/tests/file_module_test.info +++ b/modules/file/tests/file_module_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/file/tests/file_module_test.module b/modules/file/tests/file_module_test.module index ea65981caede245d1b807a7dd8021d99c418d5b6..358c8b011591e2fc8183e66d2fe374e28829ae9e 100644 --- a/modules/file/tests/file_module_test.module +++ b/modules/file/tests/file_module_test.module @@ -22,7 +22,10 @@ function file_module_test_menu() { } /** - * Form builder for testing a 'managed_file' element. + * Form constructor for testing a 'managed_file' element. + * + * @see file_module_test_form_submit() + * @ingroup forms */ function file_module_test_form($form, &$form_state, $tree = TRUE, $extended = FALSE, $default_fid = NULL) { $form['#tree'] = (bool) $tree; diff --git a/modules/filter/filter.info b/modules/filter/filter.info index 3b5d9e58002eebe2cc3efdbfb0c849ef611fb4e1..3621a7a402e4ad01330f541a259cc8c09cec0a12 100644 --- a/modules/filter/filter.info +++ b/modules/filter/filter.info @@ -7,8 +7,8 @@ files[] = filter.test required = TRUE configure = admin/config/content/formats -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/forum/forum.info b/modules/forum/forum.info index 9ebb3283ed5e6634a32e2399b2e22e3304d8ed54..6b2667991d579fda4b53dbfae05086c8a94336d7 100644 --- a/modules/forum/forum.info +++ b/modules/forum/forum.info @@ -9,8 +9,8 @@ files[] = forum.test configure = admin/structure/forum stylesheets[all][] = forum.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/help/help.admin.inc b/modules/help/help.admin.inc index 3db06ca1a24599af875f33fd7cea052d0f99069f..ec1a18e33efdea8b05c29a457c7995fe01f9365e 100644 --- a/modules/help/help.admin.inc +++ b/modules/help/help.admin.inc @@ -17,6 +17,9 @@ function help_main() { /** * Menu callback; prints a page listing general help for a module. + * + * @param $name + * A module name to display a help page for. */ function help_page($name) { $output = ''; @@ -46,6 +49,12 @@ function help_page($name) { return $output; } +/** + * Provides a formatted list of available help topics. + * + * @return + * A string containing the formatted list. + */ function help_links_as_list() { $empty_arg = drupal_help_arg(); $module_info = system_rebuild_module_data(); @@ -58,7 +67,7 @@ function help_links_as_list() { } asort($modules); - // Output pretty four-column list + // Output pretty four-column list. $count = count($modules); $break = ceil($count / 4); $output = '<div class="clearfix"><div class="help-items"><ul>'; diff --git a/modules/help/help.api.php b/modules/help/help.api.php index ff2f97c6e8c2934a58d4a322f26023739c1cd8b6..f7d9c08b689d2e21c8814e73d2ec3f3f2f438ade 100644 --- a/modules/help/help.api.php +++ b/modules/help/help.api.php @@ -42,6 +42,7 @@ * the current page's help. Note that depending on which module is invoking * hook_help, $arg may contain only empty strings. Regardless, $arg[0] to * $arg[11] will always be set. + * * @return * A localized string containing the help text. */ diff --git a/modules/help/help.info b/modules/help/help.info index 2460f0a9a085f3657722d0847052410fa51e73d4..93fcdc27f4340d34303c7ef804f818400c2ee36d 100644 --- a/modules/help/help.info +++ b/modules/help/help.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = help.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/help/help.test b/modules/help/help.test index 73b4dedc86ac2d9e18cd1fe7597af85b2db1a0c5..e72aa4a0e3603963acf2b14a9073f4eb4af553ae 100644 --- a/modules/help/help.test +++ b/modules/help/help.test @@ -5,8 +5,18 @@ * Tests for help.module. */ +/** + * Tests help display and user access for all modules implementing help. + */ class HelpTestCase extends DrupalWebTestCase { + /** + * The admin user that will be created. + */ protected $big_user; + + /** + * The anonymous user that will be created. + */ protected $any_user; public static function getInfo() { @@ -17,9 +27,6 @@ class HelpTestCase extends DrupalWebTestCase { ); } - /** - * Enable modules and create users with specific permissions. - */ function setUp() { parent::setUp('blog', 'poll'); @@ -31,7 +38,7 @@ class HelpTestCase extends DrupalWebTestCase { } /** - * Login users, create dblog events, and test dblog functionality through the admin and user interfaces. + * Logs in users, creates dblog events, and tests dblog functionality. */ function testHelp() { // Login the admin user. @@ -60,9 +67,10 @@ class HelpTestCase extends DrupalWebTestCase { } /** - * Verify the logged in user has the desired access to the various help nodes and the nodes display help. + * Verifies the logged in user has access to the various help nodes. * - * @param integer $response HTTP response code. + * @param integer $response + * An HTTP response code. */ protected function verifyHelp($response = 200) { foreach ($this->modules as $module => $name) { @@ -77,9 +85,10 @@ class HelpTestCase extends DrupalWebTestCase { } /** - * Get list of enabled modules that implement hook_help(). + * Gets the list of enabled modules that implement hook_help(). * - * @return array Enabled modules. + * @return array + * A list of enabled modules. */ protected function getModuleList() { $this->modules = array(); @@ -94,9 +103,12 @@ class HelpTestCase extends DrupalWebTestCase { } /** - * Tests module without help to verify it is not listed in help page. + * Tests a module without help to verify it is not listed in the help page. */ class NoHelpTestCase extends DrupalWebTestCase { + /** + * The user who will be created. + */ protected $big_user; public static function getInfo() { @@ -114,7 +126,7 @@ class NoHelpTestCase extends DrupalWebTestCase { } /** - * Ensure modules not implementing help do not appear on admin/help. + * Ensures modules not implementing help do not appear on admin/help. */ function testMainPageNoHelp() { $this->drupalLogin($this->big_user); diff --git a/modules/image/image.effects.inc b/modules/image/image.effects.inc index ea898f91f0fcbf6508519522718bd6d2124486df..35a6a74c7ac92578642030944f886a3096304c9b 100644 --- a/modules/image/image.effects.inc +++ b/modules/image/image.effects.inc @@ -124,13 +124,11 @@ function image_resize_dimensions(array &$dimensions, array $data) { function image_scale_effect(&$image, $data) { // Set sane default values. $data += array( + 'width' => NULL, + 'height' => NULL, 'upscale' => FALSE, ); - // Set impossibly large values if the width and height aren't set. - $data['width'] = empty($data['width']) ? PHP_INT_MAX : $data['width']; - $data['height'] = empty($data['height']) ? PHP_INT_MAX : $data['height']; - if (!image_scale($image, $data['width'], $data['height'], $data['upscale'])) { watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit, '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR); return FALSE; diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc index d08fd5c9798288c1a391fb2b486f1bd48aacef99..61a72e4c270bb2465a80c8f4b030c23cbc6786b1 100644 --- a/modules/image/image.field.inc +++ b/modules/image/image.field.inc @@ -403,7 +403,8 @@ function image_field_widget_process($element, &$form_state, $form) { '#type' => 'textfield', '#default_value' => isset($item['alt']) ? $item['alt'] : '', '#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'), - '#maxlength' => variable_get('image_alt_length', 80), // See http://www.gawds.org/show.php?contentid=28. + // @see http://www.gawds.org/show.php?contentid=28 + '#maxlength' => 512, '#weight' => -2, '#access' => (bool) $item['fid'] && $settings['alt_field'], ); @@ -412,7 +413,7 @@ function image_field_widget_process($element, &$form_state, $form) { '#title' => t('Title'), '#default_value' => isset($item['title']) ? $item['title'] : '', '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'), - '#maxlength' => variable_get('image_title_length', 500), + '#maxlength' => 1024, '#weight' => -1, '#access' => (bool) $item['fid'] && $settings['title_field'], ); diff --git a/modules/image/image.info b/modules/image/image.info index 464a0438b5f249132810c3e09c44d5da81939821..8fa7ac0f24a7183c1e8d5e5a92cabe1d16bc15e0 100644 --- a/modules/image/image.info +++ b/modules/image/image.info @@ -7,8 +7,8 @@ dependencies[] = file files[] = image.test configure = admin/config/media/image-styles -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/image/image.install b/modules/image/image.install index fc326b4e62c40be933923087ca26cee3220eb6d4..516555829b1d200ce73a3e1b10966c30b66c2898 100644 --- a/modules/image/image.install +++ b/modules/image/image.install @@ -121,13 +121,13 @@ function image_field_schema($field) { 'alt' => array( 'description' => "Alternative image text, for the image's 'alt' attribute.", 'type' => 'varchar', - 'length' => 128, + 'length' => 512, 'not null' => FALSE, ), 'title' => array( 'description' => "Image title text, for the image's 'title' attribute.", 'type' => 'varchar', - 'length' => 128, + 'length' => 1024, 'not null' => FALSE, ), 'width' => array( @@ -385,6 +385,51 @@ function image_update_7002(array &$sandbox) { $sandbox['#finished'] = count($sandbox['tables']) ? ($sandbox['processed'] / $sandbox['total']) : 1; } +/** + * Remove the variables that set alt and title length since they were not + * used for database column size and could cause PDO exceptions. + */ +function image_update_7003() { + variable_del('image_alt_length'); + variable_del('image_title_length'); +} + +/** + * Use a large setting (512 and 1024 characters) for the length of the image alt + * and title fields. + */ +function image_update_7004() { + $alt_spec = array( + 'type' => 'varchar', + 'length' => 512, + 'not null' => FALSE, + ); + + $title_spec = array( + 'type' => 'varchar', + 'length' => 1024, + 'not null' => FALSE, + ); + + $fields = _update_7000_field_read_fields(array( + 'module' => 'image', + 'storage_type' => 'field_sql_storage', + )); + + foreach ($fields as $field_name => $field) { + $tables = array( + _field_sql_storage_tablename($field), + _field_sql_storage_revision_tablename($field), + ); + $alt_column = $field['field_name'] . '_alt'; + $title_column = $field['field_name'] . '_title'; + foreach ($tables as $table) { + db_change_field($table, $alt_column, $alt_column, $alt_spec); + db_change_field($table, $title_column, $title_column, $title_spec); + } + } +} + /** * Implements hook_requirements() to check the PHP GD Library. * diff --git a/modules/image/image.test b/modules/image/image.test index a29b4f3a1d7fbacd3b3f6956e47675fb36de941b..3b7f3e7d7e0f0401f3716aeedbf963efbf54d722 100644 --- a/modules/image/image.test +++ b/modules/image/image.test @@ -192,7 +192,7 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase { $this->assertNotIdentical(FALSE, $original_uri, t('Created the generated image file.')); // Get the URL of a file that has not been generated and try to create it. - $generated_uri = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. basename($original_uri); + $generated_uri = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. drupal_basename($original_uri); $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); $generate_url = image_style_url($this->style_name, $original_uri); @@ -746,7 +746,10 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { $widget_settings = array( 'preview_image_style' => 'medium', ); - $this->createImageField($field_name, 'article', array(), $instance_settings, $widget_settings); + $field = $this->createImageField($field_name, 'article', array(), $instance_settings, $widget_settings); + $field['deleted'] = 0; + $table = _field_sql_storage_tablename($field); + $schema = drupal_get_schema($table, TRUE); $instance = field_info_instance('node', $field_name, 'article'); $this->drupalGet('node/add/article'); @@ -786,6 +789,22 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save')); $default_output = theme('image', $image_info); $this->assertRaw($default_output, t('Image displayed using user supplied alt and title attributes.')); + + // Verify that alt/title longer than allowed results in a validation error. + $test_size = 2000; + $edit = array( + $field_name . '[' . LANGUAGE_NONE . '][0][alt]' => $this->randomName($test_size), + $field_name . '[' . LANGUAGE_NONE . '][0][title]' => $this->randomName($test_size), + ); + $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save')); + $this->assertRaw(t('Alternate text cannot be longer than %max characters but is currently %length characters long.', array( + '%max' => $schema['fields'][$field_name .'_alt']['length'], + '%length' => $test_size, + ))); + $this->assertRaw(t('Title cannot be longer than %max characters but is currently %length characters long.', array( + '%max' => $schema['fields'][$field_name .'_title']['length'], + '%length' => $test_size, + ))); } /** @@ -940,7 +959,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase { // Create a style. $style = image_style_save(array('name' => 'test')); - $generated_uri = 'public://styles/test/public/'. basename($original_uri); + $generated_uri = 'public://styles/test/public/'. drupal_basename($original_uri); $url = image_style_url('test', $original_uri); $variables = array( @@ -1129,3 +1148,144 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase { $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" alt="" />', t('Expected img tag was found.')); } } + +/** + * Tests image_dimensions_scale(). + */ +class ImageDimensionsScaleTestCase extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => 'image_dimensions_scale()', + 'description' => 'Tests all control flow branches in image_dimensions_scale().', + 'group' => 'Image', + ); + } + + /** + * Tests all control flow branches in image_dimensions_scale(). + */ + function testImageDimensionsScale() { + // Define input / output datasets to test different branch conditions. + $test = array(); + + // Test branch conditions: + // - No height. + // - Upscale, don't need to upscale. + $tests[] = array( + 'input' => array( + 'dimensions' => array( + 'width' => 1000, + 'height' => 2000, + ), + 'width' => 200, + 'height' => NULL, + 'upscale' => TRUE, + ), + 'output' => array( + 'dimensions' => array( + 'width' => 200, + 'height' => 400, + ), + 'return_value' => TRUE, + ), + ); + + // Test branch conditions: + // - No width. + // - Don't upscale, don't need to upscale. + $tests[] = array( + 'input' => array( + 'dimensions' => array( + 'width' => 1000, + 'height' => 800, + ), + 'width' => NULL, + 'height' => 140, + 'upscale' => FALSE, + ), + 'output' => array( + 'dimensions' => array( + 'width' => 175, + 'height' => 140, + ), + 'return_value' => TRUE, + ), + ); + + // Test branch conditions: + // - Source aspect ratio greater than target. + // - Upscale, need to upscale. + $tests[] = array( + 'input' => array( + 'dimensions' => array( + 'width' => 8, + 'height' => 20, + ), + 'width' => 200, + 'height' => 140, + 'upscale' => TRUE, + ), + 'output' => array( + 'dimensions' => array( + 'width' => 56, + 'height' => 140, + ), + 'return_value' => TRUE, + ), + ); + + // Test branch condition: target aspect ratio greater than source. + $tests[] = array( + 'input' => array( + 'dimensions' => array( + 'width' => 2000, + 'height' => 800, + ), + 'width' => 200, + 'height' => 140, + 'upscale' => FALSE, + ), + 'output' => array( + 'dimensions' => array( + 'width' => 200, + 'height' => 80, + ), + 'return_value' => TRUE, + ), + ); + + // Test branch condition: don't upscale, need to upscale. + $tests[] = array( + 'input' => array( + 'dimensions' => array( + 'width' => 100, + 'height' => 50, + ), + 'width' => 200, + 'height' => 140, + 'upscale' => FALSE, + ), + 'output' => array( + 'dimensions' => array( + 'width' => 100, + 'height' => 50, + ), + 'return_value' => FALSE, + ), + ); + + foreach ($tests as $test) { + // Process the test dataset. + $return_value = image_dimensions_scale($test['input']['dimensions'], $test['input']['width'], $test['input']['height'], $test['input']['upscale']); + + // Check the width. + $this->assertEqual($test['output']['dimensions']['width'], $test['input']['dimensions']['width'], t('Computed width (@computed_width) equals expected width (@expected_width)', array('@computed_width' => $test['output']['dimensions']['width'], '@expected_width' => $test['input']['dimensions']['width']))); + + // Check the height. + $this->assertEqual($test['output']['dimensions']['height'], $test['input']['dimensions']['height'], t('Computed height (@computed_height) equals expected height (@expected_height)', array('@computed_height' => $test['output']['dimensions']['height'], '@expected_height' => $test['input']['dimensions']['height']))); + + // Check the return value. + $this->assertEqual($test['output']['return_value'], $return_value, t('Correct return value.')); + } + } +} diff --git a/modules/image/tests/image_module_test.info b/modules/image/tests/image_module_test.info index 3088e18339bbd47e1dea6ade631ee802e0a6fc1e..26f46dca2d32d1066222f352ff575496c2a39721 100644 --- a/modules/image/tests/image_module_test.info +++ b/modules/image/tests/image_module_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = image_module_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/locale/locale.info b/modules/locale/locale.info index 03c6d801a59bf81003c1e9bd13f1dc2bb509fa78..1b305da63634357b58fb7a25fb72b3576438dd88 100644 --- a/modules/locale/locale.info +++ b/modules/locale/locale.info @@ -6,8 +6,8 @@ core = 7.x files[] = locale.test configure = admin/config/regional/language -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 07884614a51e6b2d84ae1c91b842bbb83df46200..caa01f2f3845a202558b620fda70eef18c45a9a1 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -630,7 +630,14 @@ function locale_modules_disabled($modules) { */ function locale($string = NULL, $context = NULL, $langcode = NULL) { global $language; - $locale_t = &drupal_static(__FUNCTION__); + + // Use the advanced drupal_static() pattern, since this is called very often. + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast['locale'] = &drupal_static(__FUNCTION__); + } + $locale_t = &$drupal_static_fast['locale']; + if (!isset($string)) { // Return all cached strings if no string was specified @@ -1000,13 +1007,12 @@ function locale_url_outbound_alter(&$path, &$options, $original_path) { $negotiation = variable_get("language_negotiation_$type", array()); foreach ($negotiation as $id => $provider) { - if (isset($provider['file'])) { - require_once DRUPAL_ROOT . '/' . $provider['file']; - } - - // Avoid duplicate callback entries. if (isset($provider['callbacks']['url_rewrite'])) { - $callbacks[$provider['callbacks']['url_rewrite']] = NULL; + if (isset($provider['file'])) { + require_once DRUPAL_ROOT . '/' . $provider['file']; + } + // Avoid duplicate callback entries. + $callbacks[$provider['callbacks']['url_rewrite']] = TRUE; } } } diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 5eb1cd342f814548d438c7aaa76f8374fe6d5d1e..ca945569ac7e4946a1239ab3f4ef2838e5f48966 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -1880,6 +1880,34 @@ class LocaleContentFunctionalTest extends DrupalWebTestCase { parent::setUp('locale'); } + /** + * Verifies that machine name fields are always LTR. + */ + function testMachineNameLTR() { + // User to add and remove language. + $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages')); + + // Log in as admin. + $this->drupalLogin($admin_user); + + // Verify that the machine name field is LTR for a new content type. + $this->drupalGet('admin/structure/types/add'); + $this->assertFieldByXpath('//input[@name="type" and @dir="ltr"]', NULL, 'The machine name field is LTR when no additional language is configured.'); + + // Install the Arabic language (which is RTL) and configure as the default. + $edit = array(); + $edit['langcode'] = 'ar'; + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + + $edit = array(); + $edit['site_default'] = 'ar'; + $this->drupalPost(NULL, $edit, t('Save configuration')); + + // Verify that the machine name field is still LTR for a new content type. + $this->drupalGet('admin/structure/types/add'); + $this->assertFieldByXpath('//input[@name="type" and @dir="ltr"]', NULL, 'The machine name field is LTR when the default language is RTL.'); + } + /** * Test if a content type can be set to multilingual and language setting is * present on node add and edit forms. diff --git a/modules/locale/tests/locale_test.info b/modules/locale/tests/locale_test.info index 2dc30579b7645f048118b6370de77f9da3cbef3f..8f5237e11918f62f4d28533a1d93a985ed4638da 100644 --- a/modules/locale/tests/locale_test.info +++ b/modules/locale/tests/locale_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/menu/menu.admin.js b/modules/menu/menu.admin.js index 15bc2e7c790b751931012cd8bf1132f37ccc8b62..4fa094e715fa7bb2fd018fe2afcb0953993b16c1 100644 --- a/modules/menu/menu.admin.js +++ b/modules/menu/menu.admin.js @@ -1,47 +1,46 @@ - (function ($) { - Drupal.behaviors.menuChangeParentItems = { - attach: function (context, settings) { - $('fieldset#edit-menu input').each(function () { - $(this).change(function () { - // Update list of available parent menu items. - Drupal.menu_update_parent_list(); - }); +Drupal.behaviors.menuChangeParentItems = { + attach: function (context, settings) { + $('fieldset#edit-menu input').each(function () { + $(this).change(function () { + // Update list of available parent menu items. + Drupal.menu_update_parent_list(); }); - } + }); } +}; - /** - * Function to set the options of the menu parent item dropdown. - */ - Drupal.menu_update_parent_list = function () { - var values = []; +/** + * Function to set the options of the menu parent item dropdown. + */ +Drupal.menu_update_parent_list = function () { + var values = []; - $('input:checked', $('fieldset#edit-menu')).each(function () { - // Get the names of all checked menus. - values.push(Drupal.checkPlain($.trim($(this).val()))); - }); + $('input:checked', $('fieldset#edit-menu')).each(function () { + // Get the names of all checked menus. + values.push(Drupal.checkPlain($.trim($(this).val()))); + }); - var url = Drupal.settings.basePath + 'admin/structure/menu/parents'; - $.ajax({ - url: location.protocol + '//' + location.host + url, - type: 'POST', - data: {'menus[]' : values}, - dataType: 'json', - success: function (options) { - // Save key of last selected element. - var selected = $('fieldset#edit-menu #edit-menu-parent :selected').val(); - // Remove all exisiting options from dropdown. - $('fieldset#edit-menu #edit-menu-parent').children().remove(); - // Add new options to dropdown. - jQuery.each(options, function(index, value) { - $('fieldset#edit-menu #edit-menu-parent').append( - $('<option ' + (index == selected ? ' selected="selected"' : '') + '></option>').val(index).text(value) - ); - }); - } - }); - } + var url = Drupal.settings.basePath + 'admin/structure/menu/parents'; + $.ajax({ + url: location.protocol + '//' + location.host + url, + type: 'POST', + data: {'menus[]' : values}, + dataType: 'json', + success: function (options) { + // Save key of last selected element. + var selected = $('fieldset#edit-menu #edit-menu-parent :selected').val(); + // Remove all exisiting options from dropdown. + $('fieldset#edit-menu #edit-menu-parent').children().remove(); + // Add new options to dropdown. + jQuery.each(options, function(index, value) { + $('fieldset#edit-menu #edit-menu-parent').append( + $('<option ' + (index == selected ? ' selected="selected"' : '') + '></option>').val(index).text(value) + ); + }); + } + }); +}; })(jQuery); diff --git a/modules/menu/menu.info b/modules/menu/menu.info index c1491428e93b66391596e8a8feda0ff7de85e502..116f232b4560841d9260189f34359dc72c46be9b 100644 --- a/modules/menu/menu.info +++ b/modules/menu/menu.info @@ -6,8 +6,8 @@ core = 7.x files[] = menu.test configure = admin/structure/menu -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/menu/menu.install b/modules/menu/menu.install index a7e43379e8dc133cbcd4d30adb405dca9459c6d6..7877b996db31552dd55bf3373148d9b80a51354c 100644 --- a/modules/menu/menu.install +++ b/modules/menu/menu.install @@ -182,6 +182,28 @@ function menu_update_7002(&$sandbox) { update_fix_d7_block_deltas($sandbox, $renamed_deltas, $moved_deltas); } } +/** + * Add missing custom menus to active menus list. + */ +function menu_update_7003(&$sandbox) { + // Make sure all custom menus are present in the active menus variable so that + // their items may appear in the active trail. + // @see menu_set_active_menu_names() + $active_menus = variable_get('menu_default_active_menus', array_keys(menu_list_system_menus())); + $update_variable = FALSE; + foreach (menu_get_names() as $menu_name) { + if (!in_array($menu_name, $active_menus) && (strpos($menu_name, 'menu-') === 0)) { + $active_menus[] = $menu_name; + $update_variable = TRUE; + } + } + if ($update_variable) { + variable_set('menu_default_active_menus', $active_menus); + } + // Clear the menu cache. + cache_clear_all(NULL, 'cache_menu'); +} + /** * @} End of "defgroup updates-7.x-extra" * The next series of updates should start at 8000. diff --git a/modules/menu/menu.js b/modules/menu/menu.js index 40c1bfe95fa3b3480c94104803bf32dfdd837e72..ff4ef1e172116f3358b4341bc73fcab898f948be 100644 --- a/modules/menu/menu.js +++ b/modules/menu/menu.js @@ -1,4 +1,3 @@ - (function ($) { Drupal.behaviors.menuFieldsetSummaries = { diff --git a/modules/menu/menu.module b/modules/menu/menu.module index c9148673123c6c0021c022f3e19c072a1875a7dc..40614a162113d58533d703db3bf3a9ced6a5a24f 100644 --- a/modules/menu/menu.module +++ b/modules/menu/menu.module @@ -268,6 +268,15 @@ function menu_save($menu) { switch ($status) { case SAVED_NEW: + // Make sure the menu is present in the active menus variable so that its + // items may appear in the menu active trail. + // @see menu_set_active_menu_names() + $active_menus = variable_get('menu_default_active_menus', array_keys(menu_get_menus())); + if (!in_array($menu['menu_name'], $active_menus)) { + $active_menus[] = $menu['menu_name']; + variable_set('menu_default_active_menus', $active_menus); + } + module_invoke_all('menu_insert', $menu); break; @@ -305,6 +314,15 @@ function menu_delete($menu) { // Delete all links from the menu. menu_delete_links($menu['menu_name']); + // Remove menu from active menus variable. + $active_menus = variable_get('menu_default_active_menus', array_keys(menu_get_menus())); + foreach ($active_menus as $i => $menu_name) { + if ($menu['menu_name'] == $menu_name) { + unset($active_menus[$i]); + variable_set('menu_default_active_menus', $active_menus); + } + } + // Delete the custom menu. db_delete('menu_custom') ->condition('menu_name', $menu['menu_name']) @@ -614,7 +632,7 @@ function menu_form_node_form_alter(&$form, $form_state) { // menu_parent_options() is goofy and can actually handle either a menu link // or a node type both as second argument. Pick based on whether there is // a link already (menu_node_prepare() sets mlid default to 0). - $options = menu_parent_options(menu_get_menus(), $link['mlid'] ? $link : $type); + $options = menu_parent_options(menu_get_menus(), $link['mlid'] ? $link : $type, $type); // If no possible parent menu items were found, there is nothing to display. if (empty($options)) { return; diff --git a/modules/menu/menu.test b/modules/menu/menu.test index 0edfc47b4f0c15aa53a80d3ca7a84dee1cfb1bf8..324ba6766bcf14169312d417aa8a41d39d52ee61 100644 --- a/modules/menu/menu.test +++ b/modules/menu/menu.test @@ -205,7 +205,7 @@ class MenuTestCase extends DrupalWebTestCase { // Add menu links. $item1 = $this->addMenuLink(0, 'node/' . $node1->nid, $menu_name); - $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->nid, $menu_name); + $item2 = $this->addMenuLink($item1['mlid'], 'node/' . $node2->nid, $menu_name, FALSE); $item3 = $this->addMenuLink($item2['mlid'], 'node/' . $node3->nid, $menu_name); $this->assertMenuLink($item1['mlid'], array('depth' => 1, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => 0)); $this->assertMenuLink($item2['mlid'], array('depth' => 2, 'has_children' => 1, 'p1' => $item1['mlid'], 'p2' => $item2['mlid'], 'p3' => 0)); @@ -283,7 +283,7 @@ class MenuTestCase extends DrupalWebTestCase { * @param string $menu_name Menu name. * @return array Menu link created. */ - function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'navigation') { + function addMenuLink($plid = 0, $link = '<front>', $menu_name = 'navigation', $expanded = TRUE) { // View add menu link page. $this->drupalGet("admin/structure/menu/manage/$menu_name/add"); $this->assertResponse(200); @@ -294,7 +294,7 @@ class MenuTestCase extends DrupalWebTestCase { 'link_title' => $title, 'description' => '', 'enabled' => TRUE, // Use this to disable the menu and test. - 'expanded' => TRUE, // Setting this to true should test whether it works when we do the std_user tests. + 'expanded' => $expanded, // Setting this to true should test whether it works when we do the std_user tests. 'parent' => $menu_name . ':' . $plid, 'weight' => '0', ); @@ -345,7 +345,7 @@ class MenuTestCase extends DrupalWebTestCase { if (isset($parent)) { // Verify menu link. $title = $parent['link_title']; - $this->assertText($title, 'Parent menu link was displayed'); + $this->assertLink($title, 0, 'Parent menu link was displayed'); // Verify menu link link. $this->clickLink($title); @@ -355,7 +355,7 @@ class MenuTestCase extends DrupalWebTestCase { // Verify menu link. $title = $item['link_title']; - $this->assertText($title, 'Menu link was displayed'); + $this->assertLink($title, 0, 'Menu link was displayed'); // Verify menu link link. $this->clickLink($title); @@ -698,6 +698,8 @@ class MenuNodeTestCase extends DrupalWebTestCase { // Assert that it is not possible to set the parent of the first node to itself or the second node. $this->assertNoOption('edit-menu-parent', 'navigation:'. $item['mlid']); $this->assertNoOption('edit-menu-parent', 'navigation:'. $child_item['mlid']); + // Assert that unallowed Management menu is not available in options. + $this->assertNoOption('edit-menu-parent', 'management:0'); } /** diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 7f8e228664f82540d9dd466d8201b9874362042d..a9881d5ee140b4c8a05e921f79181a715d21d2e8 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -570,6 +570,10 @@ function hook_node_load($nodes, $types) { * block access, return NODE_ACCESS_IGNORE or simply return nothing. * Blindly returning FALSE will break other node access modules. * + * Also note that this function isn't called for node listings (e.g., RSS feeds, + * the default home page at path 'node', a recent content block, etc.) See + * @link node_access Node access rights @endlink for a full explanation. + * * @param $node * Either a node object or the machine name of the content type on which to * perform the access check. @@ -972,6 +976,7 @@ function hook_ranking() { * The node type object that is being created. */ function hook_node_type_insert($info) { + drupal_set_message(t('You have just created a content type with a machine name %type.', array('%type' => $info->type))); } /** @@ -1229,9 +1234,12 @@ function hook_validate($node, $form, &$form_state) { /** * Display a node. * - * This is a hook used by node modules. It allows a module to define a - * custom method of displaying its nodes, usually by displaying extra - * information particular to that node type. + * This hook is invoked only on the module that defines the node's content type + * (use hook_node_view() to act on all node views). + * + * This hook is invoked during node viewing after the node is fully loaded, + * so that the node type module can define a custom method for display, or + * add to the default display. * * @param $node * The node to be displayed, as returned by node_load(). @@ -1249,8 +1257,6 @@ function hook_validate($node, $form, &$form_state) { * view of the node, you might consider implementing one of these hooks * instead. * - * For a detailed usage example, see node_example.module. - * * @ingroup node_api_hooks */ function hook_view($node, $view_mode) { diff --git a/modules/node/node.info b/modules/node/node.info index 1d038aaa4a0c8ec63586086d690f5c2c9f3fd45c..b161abf4ba857a2e740a4a9d7f7dd4a37fa9d334 100644 --- a/modules/node/node.info +++ b/modules/node/node.info @@ -9,8 +9,8 @@ required = TRUE configure = admin/structure/types stylesheets[all][] = node.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/node/node.module b/modules/node/node.module index 51ec8c3062fd2def5359351a3d1d32249eb1ef24..fe8ee51690c8a357b4912563fd4079d76648eb60 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -919,7 +919,7 @@ function node_load_multiple($nids = array(), $conditions = array(), $reset = FAL * Whether to reset the node_load_multiple cache. * * @return - * A fully-populated node object. + * A fully-populated node object, or FALSE if the node is not found. */ function node_load($nid = NULL, $vid = NULL, $reset = FALSE) { $nids = (isset($nid) ? array($nid) : array()); @@ -1396,7 +1396,7 @@ function node_build_content($node, $view_mode = 'full', $langcode = NULL) { * @param $message * A flag which sets a page title relevant to the revision being viewed. * @return - * A $page element suitable for use by drupal_page_render(). + * A $page element suitable for use by drupal_render(). */ function node_show($node, $message = FALSE) { if ($message) { @@ -2790,13 +2790,14 @@ function node_search_validate($form, &$form_state) { * that this table is a list of grants; any matching row is sufficient to * grant access to the node. * - * In node listings, the process above is followed except that - * hook_node_access() is not called on each node for performance reasons and for - * proper functioning of the pager system. When adding a node listing to your - * module, be sure to use a dynamic query created by db_select() and add a tag - * of "node_access". This will allow modules dealing with node access to ensure - * only nodes to which the user has access are retrieved, through the use of - * hook_query_TAG_alter(). + * In node listings (lists of nodes generated from a select query, such as the + * default home page at path 'node', an RSS feed, a recent content block, etc.), + * the process above is followed except that hook_node_access() is not called on + * each node for performance reasons and for proper functioning of the pager + * system. When adding a node listing to your module, be sure to use a dynamic + * query created by db_select() and add a tag of "node_access". This will allow + * modules dealing with node access to ensure only nodes to which the user has + * access are retrieved, through the use of hook_query_TAG_alter(). * * Note: Even a single module returning NODE_ACCESS_DENY from hook_node_access() * will block access to the node. Therefore, implementers should take care to @@ -2961,7 +2962,6 @@ function node_node_access($node, $op, $account) { */ function node_list_permissions($type) { $info = node_type_get_type($type); - $type = check_plain($info->type); // Build standard list of node permissions for this type. $perms = array( diff --git a/modules/node/tests/node_access_test.info b/modules/node/tests/node_access_test.info index 56df2148e4edd3db162a70000f04208f6a231414..328362fdf3e9c440093ac34c3826e7571174883e 100644 --- a/modules/node/tests/node_access_test.info +++ b/modules/node/tests/node_access_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/node/tests/node_access_test.module b/modules/node/tests/node_access_test.module index 91c117a6fe148355ab667baf3fd36cc4d2889813..f2eca2e42b253b638901422d75e386661eb054b4 100644 --- a/modules/node/tests/node_access_test.module +++ b/modules/node/tests/node_access_test.module @@ -159,7 +159,7 @@ function node_access_entity_test_page() { } /** - * Implements hook_form_node_form_alter(). + * Implements hook_form_BASE_FORM_ID_alter(). */ function node_access_test_form_node_form_alter(&$form, $form_state) { // Only show this checkbox for NodeAccessBaseTableTestCase. diff --git a/modules/node/tests/node_test.info b/modules/node/tests/node_test.info index 6088f173a1d25045e9b1ef9879c67d45a93b92cb..9cf9326f9acd5eb80f0bbfcab48696cc88ada94a 100644 --- a/modules/node/tests/node_test.info +++ b/modules/node/tests/node_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/node/tests/node_test_exception.info b/modules/node/tests/node_test_exception.info index 8257e40e6420e1e2276bf4f5927accc1bd050ebb..228b9b12ac83031a4b02641fc4c6d0564cfe31c0 100644 --- a/modules/node/tests/node_test_exception.info +++ b/modules/node/tests/node_test_exception.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc index 98af518c76598317324fb204921ec219cb3d0caa..9b793d368a2ec4c48015cfaf973d5caeb0bef04c 100644 --- a/modules/openid/openid.inc +++ b/modules/openid/openid.inc @@ -617,18 +617,31 @@ function _openid_get_params($str) { * @param $fallback_prefix * An optional prefix that will be used in case no prefix is found for the * target extension namespace. + * @param $only_signed + * Return only keys that are included in the message signature in openid.sig. + * Unsigned fields may have been modified or added by other parties than the + * OpenID Provider. + * * @return * An associative array containing all the parameters in the response message * that belong to the extension. The keys are stripped from their namespace * prefix. + * * @see http://openid.net/specs/openid-authentication-2_0.html#extensions */ -function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL) { +function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL, $only_signed = FALSE) { + $signed_keys = explode(',', $response['openid.signed']); + // Find the namespace prefix. $prefix = $fallback_prefix; foreach ($response as $key => $value) { if ($value == $extension_namespace && preg_match('/^openid\.ns\.([^.]+)$/', $key, $matches)) { $prefix = $matches[1]; + if ($only_signed && !in_array('ns.' . $matches[1], $signed_keys)) { + // The namespace was defined but was not signed as required. In this + // case we do not fall back to $fallback_prefix. + $prefix = NULL; + } break; } } @@ -641,7 +654,9 @@ function openid_extract_namespace($response, $extension_namespace, $fallback_pre foreach ($response as $key => $value) { if (preg_match('/^openid\.' . $prefix . '\.(.+)$/', $key, $matches)) { $local_key = $matches[1]; - $output[$local_key] = $value; + if (!$only_signed || in_array($prefix . '.' . $local_key, $signed_keys)) { + $output[$local_key] = $value; + } } } @@ -837,8 +852,8 @@ function _openid_invalid_openid_transition($identity, $response) { // Try to extract e-mail address from Simple Registration (SREG) or // Attribute Exchanges (AX) keys. $email = ''; - $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg'); - $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax'); + $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE); + $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE); if (!empty($sreg_values['email']) && valid_email_address($sreg_values['email'])) { $email = $sreg_values['email']; } diff --git a/modules/openid/openid.info b/modules/openid/openid.info index 5b8813995b5c6880d308cbb9ac912d7e630a3597..1b399918e1128dc4fcf145cc6c74b28b2f0d7c7f 100644 --- a/modules/openid/openid.info +++ b/modules/openid/openid.info @@ -5,8 +5,8 @@ package = Core core = 7.x files[] = openid.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/openid/openid.module b/modules/openid/openid.module index f2847fc0d4e120f442e11ca3edb223f10307a766..e08d55718793f3d3cdcb1ceb23b60897e06cf561 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -185,10 +185,15 @@ function openid_form_user_register_form_alter(&$form, &$form_state) { $response = $_SESSION['openid']['response']; - // Extract Simple Registration keys from the response. - $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg'); - // Extract Attribute Exchanges keys from the response. - $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax'); + // Extract Simple Registration keys from the response. We only include + // signed keys as required by OpenID Simple Registration Extension 1.0, + // section 4. + $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE); + // Extract Attribute Exchanges keys from the response. We only include + // signed keys. This is not required by the specification, but it is + // recommended by Google, see + // http://googlecode.blogspot.com/2011/05/security-advisory-to-websites-using.html + $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE); if (!empty($sreg_values['nickname'])) { // Use the nickname returned by Simple Registration if available. diff --git a/modules/openid/openid.test b/modules/openid/openid.test index afb9068c6bc6072c3efa25321680acebd266524a..9b6b1ad5fe5d5109a5f7e9542ebf3b0365df10a7 100644 --- a/modules/openid/openid.test +++ b/modules/openid/openid.test @@ -343,17 +343,49 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { // Use a User-supplied Identity that is the URL of an XRDS document. $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE)); - // Do not sign all mandatory fields (e.g. assoc_handle). + // Respond with an invalid signature. + variable_set('openid_test_response', array('openid.sig' => 'this-is-an-invalid-signature')); + $this->submitLoginForm($identity); + $this->assertRaw('OpenID login failed.'); + + // Do not sign the mandatory field openid.assoc_handle. variable_set('openid_test_response', array('openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce')); $this->submitLoginForm($identity); $this->assertRaw('OpenID login failed.'); - // Sign all mandatory fields and some custom fields. - variable_set('openid_test_response', array('openid.foo' => 'bar', 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,foo')); + // Sign all mandatory fields and a custom field. + $keys_to_sign = array('op_endpoint', 'claimed_id', 'identity', 'return_to', 'response_nonce', 'assoc_handle', 'foo'); + $association = new stdClass(); + $association->mac_key = variable_get('mac_key'); + $response = array( + 'openid.op_endpoint' => url('openid-test/endpoint', array('absolute' => TRUE)), + 'openid.claimed_id' => $identity, + 'openid.identity' => $identity, + 'openid.return_to' => url('openid/authenticate', array('absolute' => TRUE)), + 'openid.response_nonce' => _openid_nonce(), + 'openid.assoc_handle' => 'openid-test', + 'openid.foo' => 123, + 'openid.signed' => implode(',', $keys_to_sign), + ); + $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign); + variable_set('openid_test_response', $response); $this->submitLoginForm($identity); $this->assertNoRaw('OpenID login failed.'); - } + $this->assertFieldByName('name', '', t('No username was supplied by provider.')); + $this->assertFieldByName('mail', '', t('No e-mail address was supplied by provider.')); + // Check that unsigned SREG fields are ignored. + $response = array( + 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,sreg.nickname', + 'openid.sreg.nickname' => 'john', + 'openid.sreg.email' => 'john@example.com', + ); + variable_set('openid_test_response', $response); + $this->submitLoginForm($identity); + $this->assertNoRaw('OpenID login failed.'); + $this->assertFieldByName('name', 'john', t('Username was supplied by provider.')); + $this->assertFieldByName('mail', '', t('E-mail address supplied by provider was ignored.')); + } } /** @@ -728,4 +760,41 @@ class OpenIDUnitTest extends DrupalWebTestCase { $this->assertEqual(openid_normalize('http://example.com/path#fragment'), 'http://example.com/path', t('openid_normalize() correctly normalized a URL with a fragment.')); } + + /** + * Test openid_extract_namespace(). + */ + function testOpenidExtractNamespace() { + $response = array( + 'openid.sreg.nickname' => 'john', + 'openid.ns.ext1' => OPENID_NS_SREG, + 'openid.ext1.nickname' => 'george', + 'openid.ext1.email' => 'george@example.com', + 'openid.ns.ext2' => 'http://example.com/ns/ext2', + 'openid.ext2.foo' => '123', + 'openid.ext2.bar' => '456', + 'openid.signed' => 'sreg.nickname,ns.ext1,ext1.email,ext2.foo', + ); + + $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', NULL, FALSE); + $this->assertEqual($values, array(), t('Nothing found for unused namespace.')); + + $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', 'sreg', FALSE); + $this->assertEqual($values, array('nickname' => 'john'), t('Value found for fallback prefix.')); + + $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', FALSE); + $this->assertEqual($values, array('nickname' => 'george', 'email' => 'george@example.com'), t('Namespace takes precedence over fallback prefix.')); + + // ext1.email is signed, but ext1.nickname is not. + $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE); + $this->assertEqual($values, array('email' => 'george@example.com'), t('Unsigned namespaced fields ignored.')); + + $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', FALSE); + $this->assertEqual($values, array('foo' => '123', 'bar' => '456'), t('Unsigned fields found.')); + + // ext2.foo and ext2.bar are ignored, because ns.ext2 is not signed. The + // fallback prefix is not used, because the namespace is specified. + $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', TRUE); + $this->assertEqual($values, array(), t('Unsigned fields ignored.')); + } } diff --git a/modules/openid/tests/openid_test.info b/modules/openid/tests/openid_test.info index cfee2c2bbfe0745fb94dbdebb8dc2312629611c0..424b7f1424be639d0cd81b3c46e41a97cfb40b83 100644 --- a/modules/openid/tests/openid_test.info +++ b/modules/openid/tests/openid_test.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = openid hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/openid/tests/openid_test.module b/modules/openid/tests/openid_test.module index 629dcd3356a59023591f2be7518f64e6ba69d65d..1b0de4ec5de14079e075b3457e0fe9e0a2bb29eb 100644 --- a/modules/openid/tests/openid_test.module +++ b/modules/openid/tests/openid_test.module @@ -324,9 +324,7 @@ function _openid_test_endpoint_authenticate() { // Generate unique identifier for this authentication. $nonce = _openid_nonce(); - // Generate response containing the user's identity. The openid.sreg.xxx - // entries contain profile data stored by the OpenID Provider (see OpenID - // Simple Registration Extension 1.0). + // Generate response containing the user's identity. $response = variable_get('openid_test_response', array()) + array( 'openid.ns' => OPENID_NS_2_0, 'openid.mode' => 'id_res', @@ -336,14 +334,27 @@ function _openid_test_endpoint_authenticate() { 'openid.return_to' => $_REQUEST['openid_return_to'], 'openid.response_nonce' => $nonce, 'openid.assoc_handle' => 'openid-test', - 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle', ); + if (isset($response['openid.signed'])) { + $keys_to_sign = explode(',', $response['openid.signed']); + } + else { + // Unless openid.signed is explicitly defined, all keys are signed. + $keys_to_sign = array(); + foreach ($response as $key => $value) { + // Strip off the "openid." prefix. + $keys_to_sign[] = substr($key, 7); + } + $response['openid.signed'] = implode(',', $keys_to_sign); + } + // Sign the message using the MAC key that was exchanged during association. $association = new stdClass(); $association->mac_key = variable_get('mac_key'); - $keys_to_sign = explode(',', $response['openid.signed']); - $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign); + if (!isset($response['openid.sig'])) { + $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign); + } // Put the signed message into the query string of a URL supplied by the // Relying Party, and redirect the user. diff --git a/modules/overlay/overlay-parent.js b/modules/overlay/overlay-parent.js index f9789f14aff1bef66c31fd02425d99d85a6310e6..3519e84f64fd6269e045573b0f60ed599923ddcc 100644 --- a/modules/overlay/overlay-parent.js +++ b/modules/overlay/overlay-parent.js @@ -334,7 +334,7 @@ Drupal.overlay.setFocusBefore = function ($element, document) { $placeholder.one('blur', function () { $(this).remove(); }); -} +}; /** * Check if the given link is in the administrative section of the site. @@ -961,7 +961,7 @@ Drupal.overlay._recordTabindex = function () { var $element = $(this); var tabindex = $(this).attr('tabindex'); $element.data('drupalOverlayOriginalTabIndex', tabindex); -} +}; /** * Restore an element's original tabindex. diff --git a/modules/overlay/overlay.info b/modules/overlay/overlay.info index ef0f094832d1909b37ac84865a6b1916dc39ec96..dbea8e988b9ec4eec18a9a8179b73e1af2782918 100644 --- a/modules/overlay/overlay.info +++ b/modules/overlay/overlay.info @@ -4,8 +4,8 @@ package = Core version = VERSION core = 7.x -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/path/path.info b/modules/path/path.info index 96a7068ee901f3d6dcaadb75da751856c5ab0880..2f045ddbb4066144c40f01814300922e30e37823 100644 --- a/modules/path/path.info +++ b/modules/path/path.info @@ -6,8 +6,8 @@ core = 7.x files[] = path.test configure = admin/config/search/path -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/php/php.info b/modules/php/php.info index 4995b219d727062a83b3d4c18c4ba15dbab8573d..e7858d4417b99cafe5a9b9321aceb3b2e0137263 100644 --- a/modules/php/php.info +++ b/modules/php/php.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = php.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/php/php.module b/modules/php/php.module index 37bf9a1f337f280b8bb34a6d769d795650007038..a30c607b5b70f668d9907d6a53fb0626a8ee7d59 100644 --- a/modules/php/php.module +++ b/modules/php/php.module @@ -36,23 +36,29 @@ function php_permission() { } /** - * Evaluate a string of PHP code. + * Evaluates a string of PHP code. * - * This is a wrapper around PHP's eval(). It uses output buffering to capture both - * returned and printed text. Unlike eval(), we require code to be surrounded by - * <?php ?> tags; in other words, we evaluate the code as if it were a stand-alone - * PHP file. + * This is a wrapper around PHP's eval(). It uses output buffering to capture + * both returned and printed text. Unlike eval(), we require code to be + * surrounded by <?php ?> tags; in other words, we evaluate the code as if it + * were a stand-alone PHP file. * * Using this wrapper also ensures that the PHP code which is evaluated can not * overwrite any variables in the calling code, unlike a regular eval() call. * + * This function is also used as an implementation of + * hook_filter_FILTER_process(). + * * @param $code * The code to evaluate. + * * @return - * A string containing the printed output of the code, followed by the returned - * output of the code. + * A string containing the printed output of the code, followed by the + * returned output of the code. * * @ingroup php_wrappers + * + * @see php_filter_info() */ function php_eval($code) { global $theme_path, $theme_info, $conf; @@ -82,7 +88,9 @@ function php_eval($code) { } /** - * Tips callback for php filter. + * Implements hook_filter_FILTER_tips(). + * + * @see php_filter_info() */ function _php_filter_tips($filter, $format, $long = FALSE) { global $base_url; @@ -137,4 +145,3 @@ function php_filter_info() { ); return $filters; } - diff --git a/modules/php/php.test b/modules/php/php.test index 8ead2ac02ae40346b577e83eba4ffd24f63006f9..50fb55283b432730adfa79f3ffa252293d3bdae2 100644 --- a/modules/php/php.test +++ b/modules/php/php.test @@ -6,7 +6,7 @@ */ /** - * Base PHP test case class. + * Defines a base PHP test case class. */ class PHPTestCase extends DrupalWebTestCase { protected $php_code_format; @@ -38,7 +38,7 @@ class PHPTestCase extends DrupalWebTestCase { } /** - * Create a test node with PHP code in the body. + * Creates a test node with PHP code in the body. * * @return stdObject Node object. */ @@ -60,7 +60,7 @@ class PHPFilterTestCase extends PHPTestCase { } /** - * Make sure that the PHP filter evaluates PHP code when used. + * Makes sure that the PHP filter evaluates PHP code when used. */ function testPHPFilter() { // Log in as a user with permission to use the PHP code text format. @@ -101,7 +101,7 @@ class PHPAccessTestCase extends PHPTestCase { } /** - * Make sure that user can't use the PHP filter when not given access. + * Makes sure that the user can't use the PHP filter when not given access. */ function testNoPrivileges() { // Create node with PHP filter enabled. diff --git a/modules/poll/poll.info b/modules/poll/poll.info index 2184624dfcde94181f8b43b20e76a5bf25adb616..94f5eeca67788c65701e524262e3b12ffab68ae1 100644 --- a/modules/poll/poll.info +++ b/modules/poll/poll.info @@ -6,8 +6,8 @@ core = 7.x files[] = poll.test stylesheets[all][] = poll.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/poll/poll.module b/modules/poll/poll.module index 13d2606de838f93078d9ee0c82432fda8efa1cc1..614bb92401cef3df45405c0b2563602bcff6e6ff 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -288,9 +288,10 @@ function poll_form($node, &$form_state) { // Add initial or additional choices. $existing_delta = $delta; - $weight++; for ($delta; $delta < $choice_count; $delta++) { $key = 'new:' . ($delta - $existing_delta); + // Increase the weight of each new choice. + $weight++; $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL, '', 0, $weight, $choice_count); } @@ -408,6 +409,7 @@ function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight '#maxlength' => 7, '#parents' => array('choice', $key, 'chvotes'), '#access' => user_access('administer nodes'), + '#element_validate' => array('element_validate_integer'), ); $form['weight'] = array( @@ -451,10 +453,8 @@ function poll_node_form_submit(&$form, &$form_state) { */ function poll_validate($node, $form) { if (isset($node->title)) { - // Check for at least two options and validate amount of votes: + // Check for at least two options and validate amount of votes. $realchoices = 0; - // Renumber fields - $node->choice = array_values($node->choice); foreach ($node->choice as $i => $choice) { if ($choice['chtext'] != '') { $realchoices++; @@ -586,8 +586,10 @@ function poll_update($node) { 'weight' => $choice['weight'], )) ->insertFields(array( - 'nid' => $node->nid, - 'chtext' => $choice['chtext'], + 'nid' => $node->nid, + 'chtext' => $choice['chtext'], + 'chvotes' => (int) $choice['chvotes'], + 'weight' => $choice['weight'], )) ->execute(); } diff --git a/modules/poll/poll.test b/modules/poll/poll.test index d7648a6bad29335f094e2b63528fea191aea8f90..f5c93b288e9022b1b66dc957e6a3be6d26f12e5b 100644 --- a/modules/poll/poll.test +++ b/modules/poll/poll.test @@ -24,8 +24,9 @@ class PollTestCase extends DrupalWebTestCase { function pollCreate($title, $choices, $preview = TRUE) { $this->assertTrue(TRUE, 'Create a poll'); + $admin_user = $this->drupalCreateUser(array('create poll content', 'administer nodes')); $web_user = $this->drupalCreateUser(array('create poll content', 'access content', 'edit own poll content')); - $this->drupalLogin($web_user); + $this->drupalLogin($admin_user); // Get the form first to initialize the state of the internal browser. $this->drupalGet('node/add/poll'); @@ -33,6 +34,18 @@ class PollTestCase extends DrupalWebTestCase { // Prepare a form with two choices. list($edit, $index) = $this->_pollGenerateEdit($title, $choices); + // Verify that the vote count element only allows non-negative integers. + $edit['choice[new:1][chvotes]'] = -1; + $edit['choice[new:0][chvotes]'] = $this->randomString(7); + $this->drupalPost(NULL, $edit, t('Save')); + $this->assertText(t('Negative values are not allowed.')); + $this->assertText(t('Vote count for new choice must be an integer.')); + + // Repeat steps for initializing the state of the internal browser. + $this->drupalLogin($web_user); + $this->drupalGet('node/add/poll'); + list($edit, $index) = $this->_pollGenerateEdit($title, $choices); + // Re-submit the form until all choices are filled in. if (count($choices) > 2) { while ($index < count($choices)) { @@ -88,10 +101,6 @@ class PollTestCase extends DrupalWebTestCase { } foreach ($new_choices as $k => $text) { $edit['choice[new:' . $k . '][chtext]'] = $text; - // To test poll choice weights, every new choice is sorted in front of - // existing choices. Existing/already submitted choices should keep their - // weight. - $edit['choice[new:' . $k . '][weight]'] = (- $index - $k); } return array($edit, count($already_submitted_choices) + count($new_choices)); } @@ -122,11 +131,11 @@ class PollTestCase extends DrupalWebTestCase { */ function assertPollChoiceOrder(array $choices, $index = 0, $preview = FALSE) { $expected = array(); + $weight = 0; foreach ($choices as $id => $label) { if ($id < $index) { - // The expected weight of each choice is exactly the negated id. - // @see PollTestCase::_pollGenerateEdit() - $weight = -$id; + // The expected weight of each choice is higher than the previous one. + $weight++; // Directly assert the weight form element value for this choice. $this->assertFieldByName('choice[chid:' . $id . '][weight]', $weight, t('Found choice @id with weight @weight.', array( '@id' => $id, @@ -205,11 +214,12 @@ class PollCreateTestCase extends PollTestCase { $new_option = $this->randomName(); + $vote_count = '2000'; $node->choice[] = array( 'chid' => '', 'chtext' => $new_option, - 'chvotes' => 0, - 'weight' => 0, + 'chvotes' => (int) $vote_count, + 'weight' => 1000, ); node_save($node); @@ -217,6 +227,12 @@ class PollCreateTestCase extends PollTestCase { $this->drupalGet('poll'); $this->clickLink($title); $this->assertText($new_option, 'New option found.'); + + $option = $this->xpath('//div[@id="node-1"]//div[@class="poll"]//div[@class="text"]'); + $this->assertEqual(end($option), $new_option, 'Last item is equal to new option.'); + + $votes = $this->xpath('//div[@id="node-1"]//div[@class="poll"]//div[@class="percent"]'); + $this->assertTrue(strpos(end($votes), $vote_count) > 0, t("Votes saved.")); } function testPollClose() { diff --git a/modules/profile/profile.info b/modules/profile/profile.info index 0098735ca72d069b5b912d462d23df5f71bd394e..9e05a67fce2b57961a95d36f555e817c268d9099 100644 --- a/modules/profile/profile.info +++ b/modules/profile/profile.info @@ -11,8 +11,8 @@ configure = admin/config/people/profile ; See user_system_info_alter(). hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/rdf/rdf.info b/modules/rdf/rdf.info index 17c352514320f4c038b2c6c79a9bd3dcad59b56c..4973c0f2ca7ba244391ace83ce127c2f46e3c36e 100644 --- a/modules/rdf/rdf.info +++ b/modules/rdf/rdf.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = rdf.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/rdf/rdf.module b/modules/rdf/rdf.module index ebecd4237f62064928f59a9cd2c273101074b585..70b684c68d886f53657cecab1369aaf54484d279 100644 --- a/modules/rdf/rdf.module +++ b/modules/rdf/rdf.module @@ -769,6 +769,9 @@ function rdf_field_attach_view_alter(&$output, $context) { * Implements MODULE_preprocess_HOOK(). */ function rdf_preprocess_image(&$variables) { + // Adds the RDF type for image. We cannot use the usual entity-based mapping + // to get 'foaf:Image' because image does not have its own entity type or + // bundle. $variables['attributes']['typeof'] = array('foaf:Image'); } diff --git a/modules/rdf/tests/rdf_test.info b/modules/rdf/tests/rdf_test.info index c675b7b9f6ffebc4421bfe16d9566a22c131f705..5fe89cee6e3e5816ab12c63804423b1d2bc1f96d 100644 --- a/modules/rdf/tests/rdf_test.info +++ b/modules/rdf/tests/rdf_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/search/search.api.php b/modules/search/search.api.php index 3f745bfdb0b95fe204fbdf16c6b28cc6f8e06616..534c1e8721f463e5b90dd20338285f509bd717bd 100644 --- a/modules/search/search.api.php +++ b/modules/search/search.api.php @@ -22,13 +22,11 @@ * on your module's type of content. If you want to have your content * indexed in the standard search index, your module should also implement * hook_update_index(). If your search type has settings, you can implement - * hook_search_admin() to add them to the search settings page. You can also - * alter the display of your module's search results by implementing - * hook_search_page(). You can use hook_form_FORM_ID_alter(), with - * FORM_ID set to 'search', to add fields to the search form (see - * node_form_search_form_alter() for an example). You can use - * hook_search_access() to limit access to searching, and hook_search_page() to - * override how search results are displayed. + * hook_search_admin() to add them to the search settings page. You can use + * hook_form_FORM_ID_alter(), with FORM_ID set to 'search_form', to add fields + * to the search form (see node_form_search_form_alter() for an example). + * You can use hook_search_access() to limit access to searching, + * and hook_search_page() to override how search results are displayed. * * @return * Array with optional keys: diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc index b7af4d06ace48c3f31b7febbc790b27a26b73e6c..ad4b86e896137799cb0ed2dddf4af1e85c47f1e7 100644 --- a/modules/search/search.extender.inc +++ b/modules/search/search.extender.inc @@ -122,6 +122,16 @@ class SearchQuery extends SelectQueryExtender { */ protected $multiply = array(); + /** + * Whether or not search expressions were ignored. + * + * The maximum number of AND/OR combinations exceeded can be configured to + * avoid Denial-of-Service attacks. Expressions beyond the limit are ignored. + * + * @var boolean + */ + protected $expressionsIgnored = FALSE; + /** * Sets up the search query expression. * @@ -183,7 +193,17 @@ class SearchQuery extends SelectQueryExtender { // Classify tokens. $or = FALSE; $warning = ''; + $limit_combinations = variable_get('search_and_or_limit', 7); + // The first search expression does not count as AND. + $and_count = -1; + $or_count = 0; foreach ($keywords as $match) { + if ($or_count && $and_count + $or_count >= $limit_combinations) { + // Ignore all further search expressions to prevent Denial-of-Service + // attacks using a high number of AND/OR combinations. + $this->expressionsIgnored = TRUE; + break; + } $phrase = FALSE; // Strip off phrase quotes. if ($match[2]{0} == '"') { @@ -212,6 +232,7 @@ class SearchQuery extends SelectQueryExtender { } $this->keys['positive'][] = $last; $or = TRUE; + $or_count++; continue; } // AND operator: implied, so just ignore it. @@ -231,6 +252,7 @@ class SearchQuery extends SelectQueryExtender { } else { $this->keys['positive'] = array_merge($this->keys['positive'], $words); + $and_count++; } } $or = FALSE; @@ -323,6 +345,9 @@ class SearchQuery extends SelectQueryExtender { form_set_error('keys', format_plural(variable_get('minimum_word_size', 3), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.')); return FALSE; } + if ($this->expressionsIgnored) { + drupal_set_message(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => variable_get('search_and_or_limit', 7))), 'warning'); + } $this->executedFirstPass = TRUE; if (!empty($this->words)) { diff --git a/modules/search/search.info b/modules/search/search.info index 285d56253016f4cdbda22fb0cb18db59128b89e4..72b42d353b4a1b7f991f7af1ab549aa2b9b0a170 100644 --- a/modules/search/search.info +++ b/modules/search/search.info @@ -8,8 +8,8 @@ files[] = search.test configure = admin/config/search/settings stylesheets[all][] = search.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/search/search.module b/modules/search/search.module index 518272a022f382b2ab37c5748f6712feb7873e7b..915c96fb2eafe69e3f8ad7e77d2591461850d274 100644 --- a/modules/search/search.module +++ b/modules/search/search.module @@ -1188,7 +1188,7 @@ function search_excerpt($keys, $text) { if (($s = strrpos($end, ' ')) !== FALSE) { // Account for the added spaces. $q = max($q - 1, 0); - $s = min($s, drupal_strlen($end) - 1); + $s = min($s, strlen($end) - 1); $ranges[$q] = $p + $s; $length += $p + $s - $q; $included[$key] = $p + 1; diff --git a/modules/search/search.test b/modules/search/search.test index b1edad62b02ea9636326bea8bb1f9edcdfb8fbfa..ca1dd24a3953db292533a7f4eb888ae9e29ec509 100644 --- a/modules/search/search.test +++ b/modules/search/search.test @@ -290,6 +290,20 @@ class SearchPageText extends DrupalWebTestCase { $this->drupalGet('search/node/' . $arg); $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']"); $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.'); + + // Test a search input exceeding the limit of AND/OR combinations to test + // the Denial-of-Service protection. + $limit = variable_get('search_and_or_limit', 7); + $keys = array(); + for ($i = 0; $i < $limit + 1; $i++) { + $keys[] = $this->randomName(3); + if ($i % 2 == 0) { + $keys[] = 'OR'; + } + } + $edit['keys'] = implode(' ', $keys); + $this->drupalPost('search/node', $edit, t('Search')); + $this->assertRaw(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $limit))); } } @@ -1585,6 +1599,12 @@ class SearchExcerptTestCase extends DrupalUnitTestCase { $result = preg_replace('| +|', ' ', search_excerpt('nothing', $entities)); $this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt'); $this->assertTrue(strpos($result, 'í') > 0, 'Entities are converted in excerpt'); + + // The node body that will produce this rendered $text is: + // 123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678 +‘ +‘ +‘ ‘ + $text = "<div class=\"field field-name-body field-type-text-with-summary field-label-hidden\"><div class=\"field-items\"><div class=\"field-item even\" property=\"content:encoded\"><p>123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678 +‘ +‘ +‘ ‘</p>\n</div></div></div> "; + $result = search_excerpt('HTMLTest', $text); + $this->assertFalse(empty($result), 'Rendered Multi-byte HTML encodings are not corrupted in search excerpts'); } /** diff --git a/modules/search/tests/search_embedded_form.info b/modules/search/tests/search_embedded_form.info index 64f1ca57f144499f24876d756178b1a03681755f..d2493afe41602d2be86063a435af094d913b5e27 100644 --- a/modules/search/tests/search_embedded_form.info +++ b/modules/search/tests/search_embedded_form.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/search/tests/search_extra_type.info b/modules/search/tests/search_extra_type.info index 35ae708fb9e4fa610382ba17fcf750cfab857358..b181d54bdee696e30630ffc01821c765a2090df5 100644 --- a/modules/search/tests/search_extra_type.info +++ b/modules/search/tests/search_extra_type.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/shortcut/shortcut.admin.js b/modules/shortcut/shortcut.admin.js index 5e71e6fb654638fe0422483cf6fdc23f469ca24c..453a40231abe732d30f90dbe16c35ca3eb63c34c 100644 --- a/modules/shortcut/shortcut.admin.js +++ b/modules/shortcut/shortcut.admin.js @@ -33,7 +33,7 @@ Drupal.behaviors.shortcutDrag = { tableDrag.row.prototype.onSwap = function (swappedRow) { var disabledIndex = $(table).find('tr').index($(table).find('tr.shortcut-status-disabled')) - slots - 2, count = 0; - $(table).find('tr.shortcut-status-enabled').nextAll().filter(':not(.shortcut-slot-empty)').each(function(index) { + $(table).find('tr.shortcut-status-enabled').nextAll(':not(.shortcut-slot-empty)').each(function(index) { if (index < disabledIndex) { count++; } @@ -41,7 +41,19 @@ Drupal.behaviors.shortcutDrag = { var total = slots - count; if (total == -1) { var disabled = $(table).find('tr.shortcut-status-disabled'); - disabled.after(disabled.prevAll().filter(':not(.shortcut-slot-empty)').get(0)); + // To maintain the shortcut links limit, we need to move the last + // element from the enabled section to the disabled section. + var changedRow = disabled.prevAll(':not(.shortcut-slot-empty)').not($(this.element)).get(0); + disabled.after(changedRow); + if ($(changedRow).hasClass('draggable')) { + // The dropped element will automatically be marked as changed by + // the tableDrag system. However, the row that swapped with it + // has moved to the "disabled" section, so we need to force its + // status to be disabled and mark it also as changed. + var changedRowObject = new tableDrag.row(changedRow, 'mouse', self.indentEnabled, self.maxDepth, true); + changedRowObject.markChanged(); + rowStatusChange(changedRowObject); + } } else if (total != visibleLength) { if (total > visibleLength) { @@ -59,14 +71,18 @@ Drupal.behaviors.shortcutDrag = { // Add a handler so when a row is dropped, update fields dropped into new regions. tableDrag.onDrop = function () { + rowStatusChange(this.rowObject); + return true; + }; + + function rowStatusChange(rowObject) { // Use "status-message" row instead of "status" row because // "status-{status_name}-message" is less prone to regexp match errors. - var statusRow = $(this.rowObject.element).prevAll('tr.shortcut-status').get(0); + var statusRow = $(rowObject.element).prevAll('tr.shortcut-status').get(0); var statusName = statusRow.className.replace(/([^ ]+[ ]+)*shortcut-status-([^ ]+)([ ]+[^ ]+)*/, '$2'); - var statusField = $('select.shortcut-status-select', this.rowObject.element); + var statusField = $('select.shortcut-status-select', rowObject.element); statusField.val(statusName); - return true; - }; + } tableDrag.restripeTable = function () { // :even and :odd are reversed because jQuery counts from 0 and diff --git a/modules/shortcut/shortcut.info b/modules/shortcut/shortcut.info index 2dd64b90d98ee9d42d729577b10a64bb3ca48fd0..94fb45e135acc2f9ba3382bf941834ded54d5c56 100644 --- a/modules/shortcut/shortcut.info +++ b/modules/shortcut/shortcut.info @@ -6,8 +6,8 @@ core = 7.x files[] = shortcut.test configure = admin/config/user-interface/shortcut -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 04f66ec092476a3549fccac30f34e94b92d914f7..9095e22e10713d90a6f6e028a740fd90265c3934 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -73,6 +73,18 @@ abstract class DrupalTestCase { */ protected $skipClasses = array(__CLASS__ => TRUE); + /** + * Flag to indicate whether the test has been set up. + * + * The setUp() method isolates the test from the parent Drupal site by + * creating a random prefix for the database and setting up a clean file + * storage directory. The tearDown() method then cleans up this test + * environment. We must ensure that setUp() has been run. Otherwise, + * tearDown() will act on the parent Drupal site rather than the test + * environment, destroying live data. + */ + protected $setup = FALSE; + /** * Constructor for DrupalTestCase. * @@ -127,7 +139,15 @@ abstract class DrupalTestCase { ); // Store assertion for display after the test has completed. - Database::getConnection('default', 'simpletest_original_default') + try { + $connection = Database::getConnection('default', 'simpletest_original_default'); + } + catch (DatabaseConnectionNotDefinedException $e) { + // If the test was not set up, the simpletest_original_default + // connection does not exist. + $connection = Database::getConnection('default', 'default'); + } + $connection ->insert('simpletest') ->fields($assertion) ->execute(); @@ -474,14 +494,19 @@ abstract class DrupalTestCase { ); $completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller); $this->setUp(); - try { - $this->$method(); - // Finish up. + if ($this->setup) { + try { + $this->$method(); + // Finish up. + } + catch (Exception $e) { + $this->exceptionHandler($e); + } + $this->tearDown(); } - catch (Exception $e) { - $this->exceptionHandler($e); + else { + $this->fail(t("The test cannot be executed because it has not been set up properly.")); } - $this->tearDown(); // Remove the completion check record. DrupalTestCase::deleteAssert($completion_check_id); } @@ -691,6 +716,7 @@ class DrupalUnitTestCase extends DrupalTestCase { unset($module_list['locale']); module_list(TRUE, FALSE, FALSE, $module_list); } + $this->setup = TRUE; } protected function tearDown() { @@ -1045,28 +1071,35 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Create a user with a given set of permissions. The permissions correspond to the - * names given on the privileges page. + * Create a user with a given set of permissions. * - * @param $permissions - * Array of permission names to assign to user. - * @return + * @param array $permissions + * Array of permission names to assign to user. Note that the user always + * has the default permissions derived from the "authenticated users" role. + * + * @return object|false * A fully loaded user object with pass_raw property, or FALSE if account * creation fails. */ - protected function drupalCreateUser($permissions = array('access comments', 'access content', 'post comments', 'skip comment approval')) { - // Create a role with the given permission set. - if (!($rid = $this->drupalCreateRole($permissions))) { - return FALSE; + protected function drupalCreateUser(array $permissions = array()) { + // Create a role with the given permission set, if any. + $rid = FALSE; + if ($permissions) { + $rid = $this->drupalCreateRole($permissions); + if (!$rid) { + return FALSE; + } } // Create a user assigned to that role. $edit = array(); $edit['name'] = $this->randomName(); $edit['mail'] = $edit['name'] . '@example.com'; - $edit['roles'] = array($rid => $rid); $edit['pass'] = user_password(); $edit['status'] = 1; + if ($rid) { + $edit['roles'] = array($rid => $rid); + } $account = user_save(drupal_anonymous_user(), $edit); @@ -1294,6 +1327,13 @@ class DrupalWebTestCase extends DrupalTestCase { $test_info['test_run_id'] = $this->databasePrefix; $test_info['in_child_site'] = FALSE; + // Preset the 'install_profile' system variable, so the first call into + // system_rebuild_module_data() (in drupal_install_system()) will register + // the test's profile as a module. Without this, the installation profile of + // the parent site (executing the test) is registered, and the test + // profile's hook_install() and other hook implementations are never invoked. + $conf['install_profile'] = $this->profile; + include_once DRUPAL_ROOT . '/includes/install.inc'; drupal_install_system(); @@ -1357,6 +1397,7 @@ class DrupalWebTestCase extends DrupalTestCase { variable_set('mail_system', array('default-system' => 'TestingMailSystem')); drupal_set_time_limit($this->timeLimit); + $this->setup = TRUE; } /** @@ -1631,7 +1672,16 @@ class DrupalWebTestCase extends DrupalTestCase { * An header. */ protected function curlHeaderCallback($curlHandler, $header) { - $this->headers[] = $header; + // Header fields can be extended over multiple lines by preceding each + // extra line with at least one SP or HT. They should be joined on receive. + // Details are in RFC2616 section 4. + if ($header[0] == ' ' || $header[0] == "\t") { + // Normalize whitespace between chucks. + $this->headers[] = array_pop($this->headers) . ' ' . trim($header); + } + else { + $this->headers[] = $header; + } // Errors are being sent via X-Drupal-Assertion-* headers, // generated by _drupal_log_error() in the exact form required @@ -3069,8 +3119,21 @@ class DrupalWebTestCase extends DrupalTestCase { * @return * TRUE on pass, FALSE on fail. */ - protected function assertFieldByName($name, $value = '', $message = '') { - return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message ? $message : t('Found field by name @name', array('@name' => $name)), t('Browser')); + protected function assertFieldByName($name, $value = NULL, $message = NULL) { + if (!isset($message)) { + if (!isset($value)) { + $message = t('Found field with name @name', array( + '@name' => var_export($name, TRUE), + )); + } + else { + $message = t('Found field with name @name and value @value', array( + '@name' => var_export($name, TRUE), + '@value' => var_export($value, TRUE), + )); + } + } + return $this->assertFieldByXPath($this->constructFieldXpath('name', $name), $value, $message, t('Browser')); } /** diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 0109c99b865e91729e3a3a7fc2cf3da9847ab326..ad3c5b39dc4beb0fed200edc820cf7a0c0d7d276 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -49,8 +49,8 @@ files[] = tests/upgrade/upgrade.translatable.test files[] = tests/upgrade/upgrade.upload.test files[] = tests/upgrade/upgrade.user.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index b820f530793073385e790c7d8bd782f2f72a892b..980bc146351bcd9cc818b88e73228771c4528e34 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -504,3 +504,16 @@ function simpletest_clean_results_table($test_id = NULL) { } return 0; } + +/** + * Implements hook_mail_alter(). + * + * Aborts sending of messages with ID 'simpletest_cancel_test'. + * + * @see MailTestCase::testCancelMessage() + */ +function simpletest_mail_alter(&$message) { + if ($message['id'] == 'simpletest_cancel_test') { + $message['send'] = FALSE; + } +} diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc index 696d1471887b5c7b664865a23dc707377694df22..d1a7e4aded95017fe33dde58f2e70bd76fa8ee65 100644 --- a/modules/simpletest/simpletest.pages.inc +++ b/modules/simpletest/simpletest.pages.inc @@ -254,7 +254,7 @@ function simpletest_result_form($form, &$form_state, $test_id) { $row = array(); $row[] = $assertion->message; $row[] = $assertion->message_group; - $row[] = basename($assertion->file); + $row[] = drupal_basename($assertion->file); $row[] = $assertion->line; $row[] = $assertion->function; $row[] = simpletest_result_status_image($assertion->status); diff --git a/modules/simpletest/simpletest.test b/modules/simpletest/simpletest.test index e5b6042ac4b05ab8a525bcbb58fb45bdcc118c7d..dbe2ec0f32ed3d7ccc2a769480a6ee49fbf62836 100644 --- a/modules/simpletest/simpletest.test +++ b/modules/simpletest/simpletest.test @@ -20,10 +20,7 @@ class SimpleTestFunctionalTest extends DrupalWebTestCase { public static function getInfo() { return array( 'name' => 'SimpleTest functionality', - 'description' => 'Test SimpleTest\'s web interface: check that the intended tests were - run and ensure that test reports display the intended results. Also - test SimpleTest\'s internal browser and API\'s both explicitly and - implicitly.', + 'description' => "Test SimpleTest's web interface: check that the intended tests were run and ensure that test reports display the intended results. Also test SimpleTest's internal browser and API's both explicitly and implicitly.", 'group' => 'SimpleTest' ); } @@ -503,3 +500,70 @@ class SimpleTestMissingDependentModuleUnitTest extends DrupalUnitTestCase { $this->fail(t('Running test with missing required module.')); } } + +/** + * Tests a test case that does not run parent::setUp() in its setUp() method. + * + * If a test case does not call parent::setUp(), running + * DrupalTestCase::tearDown() would destroy the main site's database tables. + * Therefore, we ensure that tests which are not set up properly are skipped. + * + * @see DrupalTestCase + */ +class SimpleTestBrokenSetUp extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'Broken SimpleTest method', + 'description' => 'Tests a test case that does not call parent::setUp().', + 'group' => 'SimpleTest' + ); + } + + function setUp() { + // If the test is being run from the main site, set up normally. + if (!drupal_valid_test_ua()) { + parent::setUp('simpletest'); + // Create and log in user. + $admin_user = $this->drupalCreateUser(array('administer unit tests')); + $this->drupalLogin($admin_user); + } + // If the test is being run from within simpletest, set up the broken test. + else { + $this->pass(t('The test setUp() method has been run.')); + // Don't call parent::setUp(). This should trigger an error message. + } + } + + function tearDown() { + // If the test is being run from the main site, tear down normally. + if (!drupal_valid_test_ua()) { + parent::tearDown(); + } + else { + // If the test is being run from within simpletest, output a message. + $this->pass(t('The tearDown() method has run.')); + } + } + + /** + * Runs this test case from within the simpletest child site. + */ + function testBreakSetUp() { + // If the test is being run from the main site, run it again from the web + // interface within the simpletest child site. + if (!drupal_valid_test_ua()) { + $edit['SimpleTestBrokenSetUp'] = TRUE; + $this->drupalPost('admin/config/development/testing', $edit, t('Run tests')); + + // Verify that the broken test and its tearDown() method are skipped. + $this->assertRaw(t('The test setUp() method has been run.')); + $this->assertRaw(t('The test cannot be executed because it has not been set up properly.')); + $this->assertNoRaw(t('The test method has run.')); + $this->assertNoRaw(t('The tearDown() method has run.')); + } + // If the test is being run from within simpletest, output a message. + else { + $this->pass(t('The test method has run.')); + } + } +} diff --git a/modules/simpletest/tests/actions_loop_test.info b/modules/simpletest/tests/actions_loop_test.info index 65d16158af07771b0a20e29bf7423b776d892c0e..007ae749b1cef61d63ac96205b1e655e01af5044 100644 --- a/modules/simpletest/tests/actions_loop_test.info +++ b/modules/simpletest/tests/actions_loop_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/ajax_forms_test.info b/modules/simpletest/tests/ajax_forms_test.info index c0c499bc79d1d7a5f87ab21d984825f8c40a587a..aa2fa9b9524d05f63fe5c379d5e208520376386d 100644 --- a/modules/simpletest/tests/ajax_forms_test.info +++ b/modules/simpletest/tests/ajax_forms_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/ajax_test.info b/modules/simpletest/tests/ajax_test.info index 303eca6b7ea5bb1772584b7f4c3c7f39bd733790..96d0a7c131d98361d1b771429b23a889880acde8 100644 --- a/modules/simpletest/tests/ajax_test.info +++ b/modules/simpletest/tests/ajax_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/batch_test.info b/modules/simpletest/tests/batch_test.info index 74ca478db7141449774c8bac99fbcd50b11b53c6..db4efe26655cd4c54435899aaeabc51621432451 100644 --- a/modules/simpletest/tests/batch_test.info +++ b/modules/simpletest/tests/batch_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test index a643ff94fa57a1bcb1ec999f2e7019a0c4f385b2..5650d8c012d7bcec68378309d2411509ef05dc74 100644 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -524,7 +524,7 @@ class CommonSizeTestCase extends DrupalUnitTestCase { /** * Test drupal_explode_tags() and drupal_implode_tags(). */ -class DrupalTagsHandlingTestCase extends DrupalWebTestCase { +class DrupalTagsHandlingTestCase extends DrupalUnitTestCase { var $validTags = array( 'Drupal' => 'Drupal', 'Drupal with some spaces' => 'Drupal with some spaces', @@ -1747,6 +1747,37 @@ class DrupalRenderTestCase extends DrupalWebTestCase { '@type' => var_export($element['#type'], TRUE), ))); } + + /** + * Tests caching of an empty render item. + */ + function testDrupalRenderCache() { + // Force a request via GET. + $request_method = $_SERVER['REQUEST_METHOD']; + $_SERVER['REQUEST_METHOD'] = 'GET'; + // Create an empty element. + $test_element = array( + '#cache' => array( + 'cid' => 'render_cache_test', + ), + '#markup' => '', + ); + + // Render the element and confirm that it goes through the rendering + // process (which will set $element['#printed']). + $element = $test_element; + drupal_render($element); + $this->assertTrue(isset($element['#printed']), t('No cache hit')); + + // Render the element again and confirm that it is retrieved from the cache + // instead (so $element['#printed'] will not be set). + $element = $test_element; + drupal_render($element); + $this->assertFalse(isset($element['#printed']), t('Cache hit')); + + // Restore the previous request method. + $_SERVER['REQUEST_METHOD'] = $request_method; + } } /** @@ -2072,7 +2103,7 @@ class DrupalErrorCollectionUnitTest extends DrupalWebTestCase { function assertError($error, $group, $function, $file, $message = NULL) { $this->assertEqual($error['group'], $group, t("Group was %group", array('%group' => $group))); $this->assertEqual($error['caller']['function'], $function, t("Function was %function", array('%function' => $function))); - $this->assertEqual(basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file))); + $this->assertEqual(drupal_basename($error['caller']['file']), $file, t("File was %file", array('%file' => $file))); if (isset($message)) { $this->assertEqual($error['message'], $message, t("Message was %message", array('%message' => $message))); } @@ -2082,7 +2113,7 @@ class DrupalErrorCollectionUnitTest extends DrupalWebTestCase { /** * Test the drupal_parse_info_file() API function. */ -class ParseInfoFilesTestCase extends DrupalWebTestCase { +class ParseInfoFilesTestCase extends DrupalUnitTestCase { public static function getInfo() { return array( 'name' => 'Parsing .info files', @@ -2341,8 +2372,10 @@ class DrupalJSONTest extends DrupalUnitTestCase { $str .= chr($i); } // Characters that must be escaped. - $html_unsafe = array('<', '>', '&'); - $html_unsafe_escaped = array('\u003c', '\u003e', '\u0026'); + // We check for unescaped " separately. + $html_unsafe = array('<', '>', '\'', '&'); + // The following are the encoded forms of: < > ' & " + $html_unsafe_escaped = array('\u003C', '\u003E', '\u0027', '\u0026', '\u0022'); // Verify there aren't character encoding problems with the source string. $this->assertIdentical(strlen($str), 128, t('A string with the full ASCII table has the correct length.')); @@ -2354,6 +2387,11 @@ class DrupalJSONTest extends DrupalUnitTestCase { $json = drupal_json_encode($str); $this->assertTrue(strlen($json) > strlen($str), t('A JSON encoded string is larger than the source string.')); + // The first and last characters should be ", and no others. + $this->assertTrue($json[0] == '"', t('A JSON encoded string begins with ".')); + $this->assertTrue($json[strlen($json) - 1] == '"', t('A JSON encoded string ends with ".')); + $this->assertTrue(substr_count($json, '"') == 2, t('A JSON encoded string contains exactly two ".')); + // Verify that encoding/decoding is reversible. $json_decoded = drupal_json_decode($json); $this->assertIdentical($str, $json_decoded, t('Encoding a string to JSON and decoding back results in the original string.')); diff --git a/modules/simpletest/tests/common_test.info b/modules/simpletest/tests/common_test.info index 665ee019a82ade6aed4c80c993f519e62e3a03b0..fa1feb89542e2e22ae4b291aaa3fff5e34889c2b 100644 --- a/modules/simpletest/tests/common_test.info +++ b/modules/simpletest/tests/common_test.info @@ -7,8 +7,8 @@ stylesheets[all][] = common_test.css stylesheets[print][] = common_test.print.css hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/common_test_cron_helper.info b/modules/simpletest/tests/common_test_cron_helper.info index 67a0c731c8827f5ccd083ee65d141943f0a7ece6..48441b211f2b5e70e124f6a791cba6863cbf75df 100644 --- a/modules/simpletest/tests/common_test_cron_helper.info +++ b/modules/simpletest/tests/common_test_cron_helper.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/database_test.info b/modules/simpletest/tests/database_test.info index b9a35895011953c26368a901fec9a46e0ebd1f9c..7e1a3c124b75c5054d452111e9d48c4a611f908e 100644 --- a/modules/simpletest/tests/database_test.info +++ b/modules/simpletest/tests/database_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test index 87d386aa7e5b10ed167e3e190a08f5626b9f96f8..7b15cf3fa949d2eb8bbd21c0122513f373d27059 100644 --- a/modules/simpletest/tests/database_test.test +++ b/modules/simpletest/tests/database_test.test @@ -2062,6 +2062,16 @@ class DatabaseSelectComplexTestCase extends DatabaseTestCase { $this->assertEqual($record->$age_field, 27, t('Correct data retrieved.')); } + function testHavingCountQuery() { + $query = db_select('test') + ->extend('PagerDefault') + ->having('age + 1 > 0'); + $query->addField('test', 'age'); + $query->addExpression('age + 1'); + $count = count($query->execute()->fetchCol()); + $this->assertEqual($count, 4, t('Counted the correct number of records.')); + } + /** * Test that countQuery properly removes 'all_fields' statements and * ordering clauses. @@ -3426,35 +3436,89 @@ class DatabaseTransactionTestCase extends DatabaseTestCase { */ function testTransactionWithDdlStatement() { // First, test that a commit works normally, even with DDL statements. - try { - $this->transactionOuterLayer('D', FALSE, TRUE); - - // Because we committed, the inserted rows should both be present. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidD'))->fetchField(); - $this->assertIdentical($saved_age, '24', t('Can retrieve DavidD row after commit.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielD'))->fetchField(); - $this->assertIdentical($saved_age, '19', t('Can retrieve DanielD row after commit.')); - // The created table should also exist. - $count = db_query('SELECT COUNT(id) FROM {database_test_1}')->fetchField(); - $this->assertIdentical($count, '0', t('Table was successfully created inside a transaction.')); - } - catch (Exception $e) { - $this->fail((string) $e); - } + $transaction = db_transaction(); + $this->insertRow('row'); + $this->executeDDLStatement(); + unset($transaction); + $this->assertRowPresent('row'); - // If we rollback the transaction, an exception might be thrown. - try { - $this->transactionOuterLayer('E', TRUE, TRUE); + // Even in different order. + $this->cleanUp(); + $transaction = db_transaction(); + $this->executeDDLStatement(); + $this->insertRow('row'); + unset($transaction); + $this->assertRowPresent('row'); + + // Even with stacking. + $this->cleanUp(); + $transaction = db_transaction(); + $transaction2 = db_transaction(); + $this->executeDDLStatement(); + unset($transaction2); + $transaction3 = db_transaction(); + $this->insertRow('row'); + unset($transaction3); + unset($transaction); + $this->assertRowPresent('row'); + + // A transaction after a DDL statement should still work the same. + $this->cleanUp(); + $transaction = db_transaction(); + $transaction2 = db_transaction(); + $this->executeDDLStatement(); + unset($transaction2); + $transaction3 = db_transaction(); + $this->insertRow('row'); + $transaction3->rollback(); + unset($transaction3); + unset($transaction); + $this->assertRowAbsent('row'); + + // The behavior of a rollback depends on the type of database server. + if (Database::getConnection()->supportsTransactionalDDL()) { + // For database servers that support transactional DDL, a rollback + // of a transaction including DDL statements should be possible. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('row'); + $this->executeDDLStatement(); + $transaction->rollback(); + unset($transaction); + $this->assertRowAbsent('row'); - // Because we rolled back, the inserted rows shouldn't be present. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidE'))->fetchField(); - $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidE row after rollback.')); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielE'))->fetchField(); - $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielE row after rollback.')); + // Including with stacking. + $this->cleanUp(); + $transaction = db_transaction(); + $transaction2 = db_transaction(); + $this->executeDDLStatement(); + unset($transaction2); + $transaction3 = db_transaction(); + $this->insertRow('row'); + unset($transaction3); + $transaction->rollback(); + unset($transaction); + $this->assertRowAbsent('row'); } - catch (Exception $e) { - // An exception also lets the test pass. - $this->assertTrue(true, t('Exception thrown on rollback after a DDL statement was executed.')); + else { + // For database servers that do not support transactional DDL, + // the DDL statement should commit the transaction stack. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('row'); + $this->executeDDLStatement(); + // Rollback the outer transaction. + try { + $transaction->rollback(); + unset($transaction); + // @TODO: an exception should be triggered here, but is not, because + // "ROLLBACK" fails silently in MySQL if there is no transaction active. + // $this->fail(t('Rolling back a transaction containing DDL should fail.')); + } + catch (DatabaseTransactionNoActiveException $e) { + $this->pass(t('Rolling back a transaction containing DDL should fail.')); + } + $this->assertRowPresent('row'); } } @@ -3469,6 +3533,24 @@ class DatabaseTransactionTestCase extends DatabaseTestCase { ->execute(); } + /** + * Execute a DDL statement. + */ + protected function executeDDLStatement() { + static $count = 0; + $table = array( + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + ), + 'primary key' => array('id'), + ); + db_create_table('database_test_' . ++$count, $table); + } + /** * Start over for a new test. */ @@ -3513,8 +3595,8 @@ class DatabaseTransactionTestCase extends DatabaseTestCase { * Test transaction stacking and commit / rollback. */ function testTransactionStacking() { - // This test won't work right if transactions are supported. - if (Database::getConnection()->supportsTransactions()) { + // This test won't work right if transactions are not supported. + if (!Database::getConnection()->supportsTransactions()) { return; } @@ -3593,26 +3675,33 @@ class DatabaseTransactionTestCase extends DatabaseTestCase { $this->insertRow('outer'); $transaction2 = db_transaction(); $this->insertRow('inner'); + $transaction3 = db_transaction(); + $this->insertRow('inner2'); // Rollback the outer transaction. try { $transaction->rollback(); unset($transaction); $this->fail(t('Rolling back the outer transaction while the inner transaction is active resulted in an exception.')); } - catch (Exception $e) { + catch (DatabaseTransactionOutOfOrderException $e) { $this->pass(t('Rolling back the outer transaction while the inner transaction is active resulted in an exception.')); } $this->assertFalse($database->inTransaction(), t('No more in a transaction after rolling back the outer transaction')); - // Try to commit the inner transaction. + // Try to commit one inner transaction. + unset($transaction3); + $this->pass(t('Trying to commit an inner transaction resulted in an exception.')); + // Try to rollback one inner transaction. try { + $transaction->rollback(); unset($transaction2); - $this->fail(t('Trying to commit the inner transaction resulted in an exception.')); + $this->fail(t('Trying to commit an inner transaction resulted in an exception.')); } - catch (Exception $e) { - $this->pass(t('Trying to commit the inner transaction resulted in an exception.')); + catch (DatabaseTransactionNoActiveException $e) { + $this->pass(t('Trying to commit an inner transaction resulted in an exception.')); } $this->assertRowAbsent('outer'); $this->assertRowAbsent('inner'); + $this->assertRowAbsent('inner2'); } } diff --git a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info index 82b257c85d6c2ae168ba1064b87b5ab4d33776e9..82e993aa40fa9807cb0bbd81929fce98a99cc931 100644 --- a/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info +++ b/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info index 0ce3baae1b63e84f953df4938644d1d666e15834..01ad2979c030e5f7d4fffcb4ea04c0466068be40 100644 --- a/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info +++ b/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/simpletest/tests/entity_cache_test.info index 47bd609a459ae0038846ad37a311132abcfc64f4..d7e2325165f05d4ec06c558ac2a54737041f3192 100644 --- a/modules/simpletest/tests/entity_cache_test.info +++ b/modules/simpletest/tests/entity_cache_test.info @@ -6,8 +6,8 @@ core = 7.x dependencies[] = entity_cache_test_dependency hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/simpletest/tests/entity_cache_test_dependency.info index 94026d8bb0c3dbe531584e322719f23611e9cbaa..af1ef755f207da4cdfd7f1bb845c733a3e583d7c 100644 --- a/modules/simpletest/tests/entity_cache_test_dependency.info +++ b/modules/simpletest/tests/entity_cache_test_dependency.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/entity_cache_test_dependency.module b/modules/simpletest/tests/entity_cache_test_dependency.module index 73a11495f58f95d1248cbdad2f57661f03ee4055..2d4b3be4d6f83489917c7302b2b0f8b99d2109fa 100644 --- a/modules/simpletest/tests/entity_cache_test_dependency.module +++ b/modules/simpletest/tests/entity_cache_test_dependency.module @@ -11,7 +11,7 @@ function entity_cache_test_dependency_entity_info() { return array( 'entity_cache_test' => array( - 'label' => 'Entity Cache Test', + 'label' => variable_get('entity_cache_test_label', 'Entity Cache Test'), ), ); } diff --git a/modules/simpletest/tests/entity_crud_hook_test.info b/modules/simpletest/tests/entity_crud_hook_test.info index 0ed70ecf0812eb4ef05aecd57cf538c86d1d4095..c23ac61b5b8f809383d2f6b689fad20e39b270ce 100644 --- a/modules/simpletest/tests/entity_crud_hook_test.info +++ b/modules/simpletest/tests/entity_crud_hook_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/entity_query.test b/modules/simpletest/tests/entity_query.test index ec951f8e26c402dec78ff58acbdac7f18ae54e06..d5e5524f2763164a6d9240c77c0e17b2e1399878 100644 --- a/modules/simpletest/tests/entity_query.test +++ b/modules/simpletest/tests/entity_query.test @@ -22,10 +22,10 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { function setUp() { parent::setUp(array('field_test')); - field_attach_create_bundle('test_entity_bundle_key', 'bundle1'); - field_attach_create_bundle('test_entity_bundle_key', 'bundle2'); - field_attach_create_bundle('test_entity', 'test_bundles'); - field_attach_create_bundle('test_entity_bundle', 'test_entity_bundle'); + field_test_create_bundle('bundle1'); + field_test_create_bundle('bundle2'); + field_test_create_bundle('test_bundle'); + field_test_create_bundle('test_entity_bundle'); $instances = array(); $this->fields = array(); @@ -1084,7 +1084,6 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->fields[0]['cardinality'] = 1; field_update_field($this->fields[0]); field_test_entity_info_translatable('test_entity', TRUE); - drupal_static_reset('field_available_languages'); // Create more items with different languages. $entity = new stdClass(); @@ -1121,7 +1120,6 @@ class EntityFieldQueryTestCase extends DrupalWebTestCase { $this->fields[0]['translatable'] = TRUE; field_update_field($this->fields[0]); field_test_entity_info_translatable('test_entity', TRUE); - drupal_static_reset('field_available_languages'); // Create more items with different languages. $entity = new stdClass(); diff --git a/modules/simpletest/tests/error_test.info b/modules/simpletest/tests/error_test.info index 64a9391c36b09d76d1df31cd7ae4535fa69767c0..39e5d237decef8a026d637a8fec42334d09658b9 100644 --- a/modules/simpletest/tests/error_test.info +++ b/modules/simpletest/tests/error_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test index 55e3b0aa33b568d3c6241c3c6e71b90238fa5f0c..4c56bfccef27cd73f41662ca7a4c907bffd63fcb 100644 --- a/modules/simpletest/tests/file.test +++ b/modules/simpletest/tests/file.test @@ -198,7 +198,9 @@ class FileTestCase extends DrupalWebTestCase { */ function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) { if (!isset($filepath)) { - $filepath = $this->randomName(); + // Prefix with non-latin characters to ensure that all file-related + // tests work with international filenames. + $filepath = 'Файл для тестирования ' . $this->randomName(); } if (!isset($scheme)) { $scheme = file_default_scheme(); @@ -214,7 +216,7 @@ class FileTestCase extends DrupalWebTestCase { $file = new stdClass(); $file->uri = $filepath; - $file->filename = basename($file->uri); + $file->filename = drupal_basename($file->uri); $file->filemime = 'text/plain'; $file->uid = 1; $file->timestamp = REQUEST_TIME; @@ -372,11 +374,11 @@ class FileValidatorTest extends DrupalWebTestCase { $this->image = new stdClass(); $this->image->uri = 'misc/druplicon.png'; - $this->image->filename = basename($this->image->uri); + $this->image->filename = drupal_basename($this->image->uri); $this->non_image = new stdClass(); $this->non_image->uri = 'misc/jquery.js'; - $this->non_image->filename = basename($this->non_image->uri); + $this->non_image->filename = drupal_basename($this->non_image->uri); } /** @@ -539,7 +541,7 @@ class FileUnmanagedSaveDataTest extends FileTestCase { // Provide a filename. $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); $this->assertTrue($filepath, t('Unnamed file saved correctly.')); - $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.')); + $this->assertEqual('asdf.txt', drupal_basename($filepath), t('File was named correctly.')); $this->assertEqual($contents, file_get_contents($filepath), t('Contents of the file are correct.')); $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664)); } @@ -666,7 +668,7 @@ class FileSaveUploadTest extends FileHookTestCase { $this->drupalPost('file-test/upload', $edit, t('Submit')); $this->assertResponse(200, t('Received a 200 response for posted test file.')); $this->assertRaw(t('You WIN!')); - $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(basename($image3_realpath)))); + $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(drupal_basename($image3_realpath)))); // Check that file_load_multiple() with no arguments returns FALSE. $this->assertFalse(file_load_multiple(), t('No files were loaded.')); @@ -2202,7 +2204,7 @@ class FileSaveDataTest extends FileHookTestCase { $this->assertTrue($result, t('Unnamed file saved correctly.')); $this->assertEqual(file_default_scheme(), file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); - $this->assertEqual($result->filename, basename($result->uri), t("Filename was set to the file's basename.")); + $this->assertEqual($result->filename, drupal_basename($result->uri), t("Filename was set to the file's basename.")); $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); @@ -2220,11 +2222,14 @@ class FileSaveDataTest extends FileHookTestCase { function testWithFilename() { $contents = $this->randomName(8); - $result = file_save_data($contents, 'public://' . 'asdf.txt'); + // Using filename with non-latin characters. + $filename = 'Текстовый файл.txt'; + + $result = file_save_data($contents, 'public://' . $filename); $this->assertTrue($result, t('Unnamed file saved correctly.')); $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); - $this->assertEqual('asdf.txt', basename($result->uri), t('File was named correctly.')); + $this->assertEqual($filename, drupal_basename($result->uri), t('File was named correctly.')); $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.')); $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); @@ -2336,7 +2341,10 @@ class FileDownloadTest extends FileTestCase { // Test generating an URL to a created file. $file = $this->createFile(); $url = file_create_url($file->uri); - $this->assertEqual($GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $file->filename, $url, t('Correctly generated a URL for a created file.')); + // URLs can't contain characters outside the ASCII set so $filename has to be + // encoded. + $filename = $GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . rawurlencode($file->filename); + $this->assertEqual($filename, $url, t('Correctly generated a URL for a created file.')); $this->drupalHead($url); $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.')); @@ -2356,16 +2364,20 @@ class FileDownloadTest extends FileTestCase { // Set file downloads to private so handler functions get called. // Create a file. - $file = $this->createFile(NULL, NULL, 'private'); + $contents = $this->randomName(8); + $file = $this->createFile(NULL, $contents, 'private'); $url = file_create_url($file->uri); // Set file_test access header to allow the download. file_test_set_return('download', array('x-foo' => 'Bar')); - $this->drupalHead($url); + $this->drupalGet($url); $headers = $this->drupalGetHeaders(); - $this->assertEqual($headers['x-foo'] , 'Bar', t('Found header set by file_test module on private download.')); + $this->assertEqual($headers['x-foo'], 'Bar', t('Found header set by file_test module on private download.')); $this->assertResponse(200, t('Correctly allowed access to a file when file_test provides headers.')); + // Test that the file transfered correctly. + $this->assertEqual($contents, $this->content, t('Contents of the file are correct.')); + // Deny access to all downloads via a -1 header. file_test_set_return('download', -1); $this->drupalHead($url); @@ -2602,7 +2614,7 @@ class FileMimeTypeTest extends DrupalWebTestCase { * Test mapping of mimetypes from filenames. */ public function testFileMimeTypeDetection() { - $prefix = 'simpletest://'; + $prefix = 'public://'; $test_case = array( 'test.jar' => 'application/java-archive', diff --git a/modules/simpletest/tests/file_test.info b/modules/simpletest/tests/file_test.info index 58baef308cfd0718b9327d2283d11a407f9b158c..996972c2e71a7dc8d2e3eac6f8fe9693b166e815 100644 --- a/modules/simpletest/tests/file_test.info +++ b/modules/simpletest/tests/file_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = file_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/file_test.module b/modules/simpletest/tests/file_test.module index b3c43e071bb4ae067f10a69d8bc860a43ae40638..1b11316f9f02943eee9537de8e000c78f7ed5de6 100644 --- a/modules/simpletest/tests/file_test.module +++ b/modules/simpletest/tests/file_test.module @@ -138,7 +138,7 @@ function _file_test_form_submit(&$form, &$form_state) { /** * Reset/initialize the history of calls to the file_* hooks. * - * @see file_test_get_calls() + * @see file_test_get_calls() * @see file_test_reset() */ function file_test_reset() { diff --git a/modules/simpletest/tests/filter_test.info b/modules/simpletest/tests/filter_test.info index 65b4fa09013b79291a28108a5ec25f4c75f73cd9..7ac5114412236273aa29602986506cd72b186c29 100644 --- a/modules/simpletest/tests/filter_test.info +++ b/modules/simpletest/tests/filter_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index fe2c1bbfbe4b0028545e319f11230be47d43265c..13fdca201c8c16a9c28ac36b6f74d7e25de8114a 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -588,9 +588,17 @@ class FormsElementsLabelsTestCase extends DrupalWebTestCase { $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]'); $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular checkboxes.")); + // Make sure the label is rendered for checkboxes. + $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-0"]/following-sibling::label[@for="edit-form-checkboxes-test-0" and @class="option"]'); + $this->assertTrue(isset($elements[0]), t("Label 0 found checkbox.")); + $elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]'); $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular radios.")); + // Make sure the label is rendered for radios. + $elements = $this->xpath('//input[@id="edit-form-radios-test-0"]/following-sibling::label[@for="edit-form-radios-test-0" and @class="option"]'); + $this->assertTrue(isset($elements[0]), t("Label 0 found radios.")); + // Exercise various defaults for checkboxes and modifications to ensure // appropriate override and correct behaviour. $elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]'); diff --git a/modules/simpletest/tests/form_test.info b/modules/simpletest/tests/form_test.info index 4c0b67e7338c643163639ee33c67842087fc98ce..4df575599d4940151235c832212d7c88e4700f2c 100644 --- a/modules/simpletest/tests/form_test.info +++ b/modules/simpletest/tests/form_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index 23aca244b8ea9869292898ffe644d315d78ecbda..0a748d2dfa69eefc4a09f7094257e556f188fb1c 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -688,6 +688,7 @@ function form_label_test_form() { 'first-checkbox' => t('First checkbox'), 'second-checkbox' => t('Second checkbox'), 'third-checkbox' => t('Third checkbox'), + '0' => t('0'), ), ); $form['form_radios_test'] = array( @@ -697,6 +698,7 @@ function form_label_test_form() { 'first-radio' => t('First radio'), 'second-radio' => t('Second radio'), 'third-radio' => t('Third radio'), + '0' => t('0'), ), // Test #field_prefix and #field_suffix placement. '#field_prefix' => '<span id="form-test-radios-field-prefix">' . t('Radios #field_prefix element') . '</span>', diff --git a/modules/simpletest/tests/image_test.info b/modules/simpletest/tests/image_test.info index a939da876b44c2741dad758bd96985539db2c4ce..046780b86396be20e86b814d7c7113c997cdc837 100644 --- a/modules/simpletest/tests/image_test.info +++ b/modules/simpletest/tests/image_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/mail.test b/modules/simpletest/tests/mail.test index a6c7b40e5ef3b2eb46fb79be440ddf39ddb3787c..09dcde60c0db8b0ffa0ca512289678d27d11b1a0 100644 --- a/modules/simpletest/tests/mail.test +++ b/modules/simpletest/tests/mail.test @@ -22,7 +22,7 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface { } function setUp() { - parent::setUp(); + parent::setUp(array('simpletest')); // Set MailTestCase (i.e. this class) as the SMTP library variable_set('mail_system', array('default-system' => 'MailTestCase')); @@ -35,10 +35,28 @@ class MailTestCase extends DrupalWebTestCase implements MailSystemInterface { global $language; // Use MailTestCase for sending a message. - $message = drupal_mail('simpletest', 'mail_test', 'testing@drupal.org', $language); + $message = drupal_mail('simpletest', 'mail_test', 'testing@example.com', $language); // Assert whether the message was sent through the send function. - $this->assertEqual(self::$sent_message['to'], 'testing@drupal.org', t('Pluggable mail system is extendable.')); + $this->assertEqual(self::$sent_message['to'], 'testing@example.com', t('Pluggable mail system is extendable.')); + } + + /** + * Test that message sending may be canceled. + * + * @see simpletest_mail_alter() + */ + function testCancelMessage() { + global $language; + + // Reset the class variable holding a copy of the last sent message. + self::$sent_message = NULL; + + // Send a test message that simpletest_mail_alter should cancel. + $message = drupal_mail('simpletest', 'cancel_test', 'cancel@example.com', $language); + + // Assert that the message was not actually sent. + $this->assertNull(self::$sent_message, 'Message was canceled.'); } /** diff --git a/modules/simpletest/tests/menu_test.info b/modules/simpletest/tests/menu_test.info index 17bfba065462a9ac77104828f50e722fbfc9e3c7..7f82eb2036a99b5ad0a362b866e5b34bed76f702 100644 --- a/modules/simpletest/tests/menu_test.info +++ b/modules/simpletest/tests/menu_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/module_test.info b/modules/simpletest/tests/module_test.info index 21e27657d593cd3a35cdce5aa85995959cf7a40b..3345c9275a7f21ae1197bf9c910d94068a575a67 100644 --- a/modules/simpletest/tests/module_test.info +++ b/modules/simpletest/tests/module_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/requirements1_test.info b/modules/simpletest/tests/requirements1_test.info index 1263643d3b4683aa4de1e871c1d9f6cd1d13f440..fca1c6e5cd47b4a361ffa428c0e7d9e783504db3 100644 --- a/modules/simpletest/tests/requirements1_test.info +++ b/modules/simpletest/tests/requirements1_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/requirements2_test.info b/modules/simpletest/tests/requirements2_test.info index b2c7b752e0f1bca52f023e91e0742ddcaf37c8b4..fce15e6e25c81c06e23407554e11e068f00b240b 100644 --- a/modules/simpletest/tests/requirements2_test.info +++ b/modules/simpletest/tests/requirements2_test.info @@ -7,8 +7,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/session_test.info b/modules/simpletest/tests/session_test.info index 526498e630eca2708c7cb6c02312677153f430d6..0f0b60cfe5feaa465bdf99ddf2be477bc00b175f 100644 --- a/modules/simpletest/tests/session_test.info +++ b/modules/simpletest/tests/session_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/system_dependencies_test.info b/modules/simpletest/tests/system_dependencies_test.info index 1882fd70448b119750c401700602011ef2e0e568..771cc81aa61a4614a8480b36765abd1526f6d576 100644 --- a/modules/simpletest/tests/system_dependencies_test.info +++ b/modules/simpletest/tests/system_dependencies_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = _missing_dependency -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info index 20da4cd2510a41593ce4290e1f807f5be060f60e..bab6df46a15af0f605dcebd4a69b0feaa39f6cd8 100644 --- a/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info +++ b/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = system_incompatible_core_version_test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/system_incompatible_core_version_test.info b/modules/simpletest/tests/system_incompatible_core_version_test.info index 59465a9131fe37351e6dc9047b82db991c9c8a03..11ad0318117c907656242e3a3e6dd7a5f8363ca4 100644 --- a/modules/simpletest/tests/system_incompatible_core_version_test.info +++ b/modules/simpletest/tests/system_incompatible_core_version_test.info @@ -5,8 +5,8 @@ version = VERSION core = 5.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info index 3dd555f6070cc9048d5e5d9bb3b25c70c16b5fef..1c71d7120623222cfa3ab2d04ebc2170941ef36a 100644 --- a/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info +++ b/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info @@ -7,8 +7,8 @@ hidden = TRUE ; system_incompatible_module_version_test declares version 1.0 dependencies[] = system_incompatible_module_version_test (>2.0) -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/system_incompatible_module_version_test.info b/modules/simpletest/tests/system_incompatible_module_version_test.info index aeac5673dbceadf70edf78d991df660dcfa2ad7f..19cdca6430cb94cb9040e2ec73f052bad35733fb 100644 --- a/modules/simpletest/tests/system_incompatible_module_version_test.info +++ b/modules/simpletest/tests/system_incompatible_module_version_test.info @@ -5,8 +5,8 @@ version = 1.0 core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/system_test.info b/modules/simpletest/tests/system_test.info index e809e78cd1a1fc7d6fe126346b0e327a429fb6d9..9fbf2ac30a2184e3267303f0783b16495f19b17c 100644 --- a/modules/simpletest/tests/system_test.info +++ b/modules/simpletest/tests/system_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = system_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/taxonomy_test.info b/modules/simpletest/tests/taxonomy_test.info index e93cbf5f6ddb701180d4e40b7136225e1e61ab14..5f0db2919b782ffdcf02c10a90f211609345fff9 100644 --- a/modules/simpletest/tests/taxonomy_test.info +++ b/modules/simpletest/tests/taxonomy_test.info @@ -6,8 +6,8 @@ core = 7.x hidden = TRUE dependencies[] = taxonomy -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index ea0f257dd380d9727d8c6ed73c5254e20ada5dd1..af1141124eab9cb8e2dbcf2d4d3cfbad1c20880f 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -9,6 +9,8 @@ * Unit tests for the Theme API. */ class ThemeUnitTest extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Theme API', @@ -380,3 +382,54 @@ class ThemeHtmlTag extends DrupalUnitTestCase { $this->assertEqual('<title>title test</title>'."\n", theme_html_tag($tag), t('Test title tag generation.')); } } + +/** + * Tests for the ThemeRegistry class. + */ +class ThemeRegistryTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'ThemeRegistry', + 'description' => 'Tests the behavior of the ThemeRegistry class', + 'group' => 'Theme', + ); + } + function setUp() { + parent::setUp('theme_test'); + } + + /** + * Tests the behavior of the theme registry class. + */ + function testRaceCondition() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $cid = 'test_theme_registry'; + + // Directly instantiate the theme registry, this will cause a base cache + // entry to be written in __construct(). + $registry = new ThemeRegistry($cid, 'cache'); + + $this->assertTrue(cache_get($cid), 'Cache entry was created.'); + + // Trigger a cache miss for an offset. + $this->assertTrue($registry['theme_test_template_test'], 'Offset was returned correctly from the theme registry.'); + // This will cause the ThemeRegistry class to write an updated version of + // the cache entry when it is destroyed, usually at the end of the request. + // Before that happens, manually delete the cache entry we created earlier + // so that the new entry is written from scratch. + cache_clear_all($cid, 'cache'); + + // Destroy the class so that it triggers a cache write for the offset. + unset($registry); + + $this->assertTrue(cache_get($cid), 'Cache entry was created.'); + + // Create a new instance of the class. Confirm that both the offset + // requested previously, and one that has not yet been requested are both + // available. + $registry = new ThemeRegistry($cid, 'cache'); + + $this->assertTrue($registry['theme_test_template_test'], 'Offset was returned correctly from the theme registry'); + $this->assertTrue($registry['theme_test_template_test_2'], 'Offset was returned correctly from the theme registry'); + } +} diff --git a/modules/simpletest/tests/theme_test.info b/modules/simpletest/tests/theme_test.info index ef45abe91b4d041a4a2d05fd81a75b9b7bfac8b5..8e24039ab40f2b5c605d8e3a12ac6b8b2e812a72 100644 --- a/modules/simpletest/tests/theme_test.info +++ b/modules/simpletest/tests/theme_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module index 160d192dd82a565858bdafc82dab953b2b952ef9..48e2e83c64a347e40ecee41c41272192f13305da 100644 --- a/modules/simpletest/tests/theme_test.module +++ b/modules/simpletest/tests/theme_test.module @@ -1,5 +1,27 @@ <?php +/** + * Implements hook_theme(). + */ +function theme_test_theme($existing, $type, $theme, $path) { + $items['theme_test_template_test'] = array( + 'template' => 'theme_test.template_test', + ); + $items['theme_test_template_test_2'] = array( + 'template' => 'theme_test.template_test', + ); + + return $items; +} + +/** + * Implements hook_system_theme_info(). + */ +function theme_test_system_theme_info() { + $themes['test_theme'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme/test_theme.info'; + return $themes; +} + /** * Implements hook_menu(). */ diff --git a/modules/simpletest/tests/theme_test.template_test.tpl.php b/modules/simpletest/tests/theme_test.template_test.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..cde8faadd3c38e5b5f555af2a963ffe10d647647 --- /dev/null +++ b/modules/simpletest/tests/theme_test.template_test.tpl.php @@ -0,0 +1,2 @@ +<!-- Output for Theme API test --> +Fail: Template not overridden. diff --git a/themes/tests/test_theme/template.php b/modules/simpletest/tests/themes/test_theme/template.php similarity index 100% rename from themes/tests/test_theme/template.php rename to modules/simpletest/tests/themes/test_theme/template.php diff --git a/themes/tests/test_theme/test_theme.info b/modules/simpletest/tests/themes/test_theme/test_theme.info similarity index 89% rename from themes/tests/test_theme/test_theme.info rename to modules/simpletest/tests/themes/test_theme/test_theme.info index 0adb8457ca6e511d6ac999573ce61ffb2a4a0bbb..4c5c515112b8615cc95db6b80211d6739534eb5f 100644 --- a/themes/tests/test_theme/test_theme.info +++ b/modules/simpletest/tests/themes/test_theme/test_theme.info @@ -15,8 +15,8 @@ hidden = TRUE ; file within the theme folder. stylesheets[all][] = system.base.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/unicode.test b/modules/simpletest/tests/unicode.test index 47a4938fe85698a78bc5fb2b66e97f17e1f2596e..cf7991b6cdca054125d5adb6b78763e7e98c853e 100644 --- a/modules/simpletest/tests/unicode.test +++ b/modules/simpletest/tests/unicode.test @@ -8,7 +8,7 @@ /** * Test unicode handling features implemented in unicode.inc. */ -class UnicodeUnitTest extends DrupalWebTestCase { +class UnicodeUnitTest extends DrupalUnitTestCase { /** * Whether to run the extended version of the tests (including non latin1 characters). diff --git a/modules/simpletest/tests/update_script_test.info b/modules/simpletest/tests/update_script_test.info index 5dfa1409d423689781dc596e404b497d70132423..59f1c98357ba3bbd710ce5dc25dad09cd47eca1d 100644 --- a/modules/simpletest/tests/update_script_test.info +++ b/modules/simpletest/tests/update_script_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/update_test_1.info b/modules/simpletest/tests/update_test_1.info index 8e36bd33eaa6096c2436e6838fb4385960ff8944..23f6ef1fdfb0bf01c5751e9c81b7ad0511814f81 100644 --- a/modules/simpletest/tests/update_test_1.info +++ b/modules/simpletest/tests/update_test_1.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/update_test_2.info b/modules/simpletest/tests/update_test_2.info index 8e36bd33eaa6096c2436e6838fb4385960ff8944..23f6ef1fdfb0bf01c5751e9c81b7ad0511814f81 100644 --- a/modules/simpletest/tests/update_test_2.info +++ b/modules/simpletest/tests/update_test_2.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/update_test_3.info b/modules/simpletest/tests/update_test_3.info index 8e36bd33eaa6096c2436e6838fb4385960ff8944..23f6ef1fdfb0bf01c5751e9c81b7ad0511814f81 100644 --- a/modules/simpletest/tests/update_test_3.info +++ b/modules/simpletest/tests/update_test_3.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php b/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php new file mode 100644 index 0000000000000000000000000000000000000000..1dc1946b39aa2b08054d8e49c1f028b2858ce7e1 --- /dev/null +++ b/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php @@ -0,0 +1,34 @@ +<?php +db_insert('comments')->fields(array( + 'cid', + 'pid', + 'nid', + 'uid', + 'subject', + 'comment', + 'hostname', + 'timestamp', + 'status', + 'format', + 'thread', + 'name', + 'mail', + 'homepage', +)) +->values(array( + 'cid' => 1, + 'pid' => 0, + 'nid' => 37, + 'uid' => 3, + 'subject' => 'Comment title 1', + 'comment' => 'Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1 - Comment body 1', + 'hostname' => '127.0.0.1', + 'timestamp' => 1008617630, + 'status' => 0, + 'format' => 1, + 'thread' => '01/', + 'name' => NULL, + 'mail' => NULL, + 'homepage' => '', +)) +->execute(); diff --git a/modules/simpletest/tests/upgrade/upgrade.node.test b/modules/simpletest/tests/upgrade/upgrade.node.test index cd44790c72883ca4b0bd66ba1b47b14c68f7c8b9..b49a40b0d6c5c2a17e68749a4fc065b7624dfb31 100644 --- a/modules/simpletest/tests/upgrade/upgrade.node.test +++ b/modules/simpletest/tests/upgrade/upgrade.node.test @@ -50,6 +50,38 @@ class NodeBodyUpgradePathTestCase extends UpgradePathTestCase { } } +/** + * Tests the upgrade path for node disabled node types. + * + * Load a filled installation of Drupal 6 and run the upgrade process on it. + */ +class DisabledNodeTypeTestCase extends UpgradePathTestCase { + public static function getInfo() { + return array( + 'name' => 'Disabled node type upgrade path', + 'description' => 'Disabled node type upgrade path tests.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.filled.database.php', + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-6.node_type_broken.database.php', + ); + parent::setUp(); + } + + /** + * Tests a successful upgrade. + */ + public function testDisabledNodeTypeUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + $this->assertTrue(field_info_instance('comment', 'comment_body', 'comment_node_broken'), 'Comment body field instance was created for comments attached to the disabled broken node type'); + } +} + /** * Upgrade test for node type poll. * diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test index 01e1806cd6761a7528d139c0e4d1b8ab76d49544..2602b09a2e205409d229bf260a2785781ec1d129 100644 --- a/modules/simpletest/tests/upgrade/upgrade.test +++ b/modules/simpletest/tests/upgrade/upgrade.test @@ -171,6 +171,7 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { $this->variable_set('site_mail', 'simpletest@example.com'); drupal_set_time_limit($this->timeLimit); + $this->setup = TRUE; } /** diff --git a/modules/simpletest/tests/url_alter_test.info b/modules/simpletest/tests/url_alter_test.info index b266c3b47d9efe483366894039603789dc0ee253..5ae57a77bbbedc48c1a3a89d1630baa657a3e4fd 100644 --- a/modules/simpletest/tests/url_alter_test.info +++ b/modules/simpletest/tests/url_alter_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/simpletest/tests/xmlrpc_test.info b/modules/simpletest/tests/xmlrpc_test.info index 96c02f1c8d9b8b8d38d0706f71f6d02e0b791725..9a5093bf8ecf0ea2c9a7b4d49b170a5569511349 100644 --- a/modules/simpletest/tests/xmlrpc_test.info +++ b/modules/simpletest/tests/xmlrpc_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/statistics/statistics.info b/modules/statistics/statistics.info index 60841f6a7a81f173203af2bc8132bb9cc0ee4fa1..f7205015187a5aa6439236fb1a866dc88e66559e 100644 --- a/modules/statistics/statistics.info +++ b/modules/statistics/statistics.info @@ -6,8 +6,8 @@ core = 7.x files[] = statistics.test configure = admin/config/system/statistics -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/syslog/syslog.info b/modules/syslog/syslog.info index 8a97ef00545cbe0df4215644e32f2831d5d09ada..2e8384b48d939b2849d3ecbd42b3b31f5c204938 100644 --- a/modules/syslog/syslog.info +++ b/modules/syslog/syslog.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x files[] = syslog.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/syslog/syslog.module b/modules/syslog/syslog.module index c4ee382528892a67980e328b83aefa63596427d9..7c7bf40b74b7491a36f465718fcd4e360f6d2ea1 100644 --- a/modules/syslog/syslog.module +++ b/modules/syslog/syslog.module @@ -6,6 +6,15 @@ */ if (defined('LOG_LOCAL0')) { + /** + * Sets the proper logging facility. + * + * Note that LOG_LOCAL0 through LOG_LOCAL7 are not available on Windows, so we + * check for availability. If LOG_LOCAL0 is defined by the PHP environment, we + * set that as the default; if not, we use LOG_USER. + * + * @see http://php.net/manual/function.syslog.php + */ define('DEFAULT_SYSLOG_FACILITY', LOG_LOCAL0); } else { diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 63685906223945259cf8164c99a9972688adfe4e..7ed232a547090bbc99c79e1c5b821e7c3d6ad497 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -622,7 +622,7 @@ function system_theme_settings_validate($form, &$form_state) { } else { // File upload failed. - form_set_error('logo_upload', t('The favicon could not be uploaded.')); + form_set_error('favicon_upload', t('The favicon could not be uploaded.')); } } diff --git a/modules/system/system.api.php b/modules/system/system.api.php index f241eea7857956d0f9264455cd89936825e43b1b..cb67268ca2f193ae7f89747b03ff806e3c6b306a 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1019,7 +1019,11 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * @endcode * This 'abc' object will then be passed into the callback functions defined * for the menu item, such as the page callback function mymodule_abc_edit() - * to replace the integer 1 in the argument array. + * to replace the integer 1 in the argument array. Note that a load function + * should return FALSE when it is unable to provide a loadable object. For + * example, the node_load() function for the 'node/%node/edit' menu item will + * return FALSE for the path 'node/999/edit' if a node with a node ID of 999 + * does not exist. The menu routing system will return a 404 error in this case. * * You can also define a %wildcard_to_arg() function (for the example menu * entry above this would be 'mymodule_abc_to_arg()'). The _to_arg() function @@ -1579,12 +1583,21 @@ function hook_page_alter(&$page) { * One popular use of this hook is to add form elements to the node form. When * altering a node form, the node object can be accessed at $form['#node']. * - * Note that instead of hook_form_alter(), which is called for all forms, you - * can also use hook_form_FORM_ID_alter() to alter a specific form. For each - * module (in system weight order) the general form alter hook implementation - * is invoked first, then the form ID specific alter implementation is called. - * After all module hook implementations are invoked, the hook_form_alter() - * implementations from themes are invoked in the same manner. + * In addition to hook_form_alter(), which is called for all forms, there are + * two more specific form hooks available. The first, + * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base + * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to + * target a specific form directly. + * + * The call order is as follows: all existing form alter functions are called + * for module A, then all for module B, etc., followed by all for any base + * theme(s), and finally for the theme itself. The module order is determined + * by system weight, then by module name. + * + * Within each module, form alter hooks are called in the following order: + * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third, + * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are + * called first followed by the more specific. * * @param $form * Nested array of form elements that comprise the form. @@ -1596,6 +1609,7 @@ function hook_page_alter(&$page) { * String representing the name of the form itself. Typically this is the * name of the function that generated the form. * + * @see hook_form_BASE_FORM_ID_alter() * @see hook_form_FORM_ID_alter() */ function hook_form_alter(&$form, &$form_state, $form_id) { @@ -1616,6 +1630,10 @@ function hook_form_alter(&$form, &$form_state, $form_id) { * rather than implementing hook_form_alter() and checking the form ID, or * using long switch statements to alter multiple forms. * + * Form alter hooks are called in the following order: hook_form_alter(), + * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See + * hook_form_alter() for more details. + * * @param $form * Nested array of form elements that comprise the form. * @param $form_state @@ -1627,6 +1645,7 @@ function hook_form_alter(&$form, &$form_state, $form_id) { * name of the function that generated the form. * * @see hook_form_alter() + * @see hook_form_BASE_FORM_ID_alter() * @see drupal_prepare_form() */ function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) { @@ -1643,17 +1662,27 @@ function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) { } /** - * Provide a form-specific alteration for shared forms. + * Provide a form-specific alteration for shared ('base') forms. + * + * By default, when drupal_get_form() is called, Drupal looks for a function + * with the same name as the form ID, and uses that function to build the form. + * In contrast, base forms allow multiple form IDs to be mapped to a single base + * (also called 'factory') form function. * * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific - * form belonging to multiple form_ids, rather than implementing - * hook_form_alter() and checking for conditions that would identify the - * shared form constructor. + * base form, rather than implementing hook_form_alter() and checking for + * conditions that would identify the shared form constructor. + * + * To identify the base form ID for a particular form (or to determine whether + * one exists) check the $form_state. The base form ID is stored under + * $form_state['build_info']['base_form_id']. * - * Examples for such forms are node_form() or comment_form(). + * See hook_forms() for more information on how to implement base forms in + * Drupal. * - * Note that this hook fires after hook_form_FORM_ID_alter() and before - * hook_form_alter(). + * Form alter hooks are called in the following order: hook_form_alter(), + * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See + * hook_form_alter() for more details. * * @param $form * Nested array of form elements that comprise the form. @@ -1663,8 +1692,10 @@ function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) { * String representing the name of the form itself. Typically this is the * name of the function that generated the form. * + * @see hook_form_alter() * @see hook_form_FORM_ID_alter() * @see drupal_prepare_form() + * @see hook_forms() */ function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) { // Modification for the form with the given BASE_FORM_ID goes here. For @@ -1684,13 +1715,25 @@ function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) { * * By default, when drupal_get_form() is called, the system will look for a * function with the same name as the form ID, and use that function to build - * the form. This hook allows you to override that behavior in two ways. + * the form. If no such function is found, Drupal calls this hook. Modules + * implementing this hook can then provide their own instructions for mapping + * form IDs to constructor functions. As a result, you can easily map multiple + * form IDs to a single form constructor (referred to as a 'base' form). + * + * Using a base form can help to avoid code duplication, by allowing many + * similar forms to use the same code base. Another benefit is that it becomes + * much easier for other modules to apply a general change to the group of + * forms; hook_form_BASE_FORM_ID_alter() can be used to easily alter multiple + * forms at once by directly targeting the shared base form. + * + * Two example use cases where base forms may be useful are given below. * * First, you can use this hook to tell the form system to use a different * function to build certain forms in your module; this is often used to define * a form "factory" function that is used to build several similar forms. In * this case, your hook implementation will likely ignore all of the input - * arguments. See node_forms() for an example of this. + * arguments. See node_forms() for an example of this. Note, node_forms() is the + * hook_forms() implementation; the base form itself is defined in node_form(). * * Second, you could use this hook to define how to build a form with a * dynamically-generated form ID. In this case, you would need to verify that @@ -1707,7 +1750,9 @@ function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) { * @return * An associative array whose keys define form_ids and whose values are an * associative array defining the following keys: - * - callback: The name of the form builder function to invoke. + * - callback: The name of the form builder function to invoke. This will be + * used for the base form ID, for example, to target a base form using + * hook_form_BASE_FORM_ID_alter(). * - callback arguments: (optional) Additional arguments to pass to the * function defined in 'callback', which are prepended to $args. * - wrapper_callback: (optional) The name of a form builder function to @@ -1862,12 +1907,20 @@ function hook_image_toolkits() { * - 'language': * The language object used to build the message before hook_mail_alter() * is invoked. + * - 'send': + * Set to FALSE to abort sending this email message. * * @see drupal_mail() */ function hook_mail_alter(&$message) { if ($message['id'] == 'modulename_messagekey') { - $message['body'][] = "--\nMail sent out from " . variable_get('sitename', t('Drupal')); + if (!example_notifications_optin($message['to'], $message['id'])) { + // If the recipient has opted to not receive such messages, cancel + // sending. + $message['send'] = FALSE; + return; + } + $message['body'][] = "--\nMail sent out from " . variable_get('site_name', t('Drupal')); } } @@ -1906,6 +1959,25 @@ function hook_module_implements_alter(&$implementations, $hook) { } } +/** + * Return additional themes provided by modules. + * + * Only use this hook for testing purposes. Use a hidden MYMODULE_test.module + * to implement this hook. Testing themes should be hidden, too. + * + * This hook is invoked from _system_rebuild_theme_data() and allows modules to + * register additional themes outside of the regular 'themes' directories of a + * Drupal installation. + * + * @return + * An associative array. Each key is the system name of a theme and each value + * is the corresponding path to the theme's .info file. + */ +function hook_system_theme_info() { + $themes['mymodule_test_theme'] = drupal_get_path('module', 'mymodule') . '/mymodule_test_theme/mymodule_test_theme.info'; + return $themes; +} + /** * Alter the information parsed from module and theme .info files * @@ -2593,7 +2665,7 @@ function hook_stream_wrappers_alter(&$wrappers) { * An array of file objects, indexed by fid. * * @see file_load_multiple() - * @see upload_file_load() + * @see file_load() */ function hook_file_load($files) { // Add the upload specific data into the file object. @@ -2718,7 +2790,6 @@ function hook_file_move($file, $source) { * The file that has just been deleted. * * @see file_delete() - * @see upload_file_delete() */ function hook_file_delete($file) { // Delete all information associated with the file. @@ -3171,14 +3242,11 @@ function hook_install() { * * A good rule of thumb is to remove updates older than two major releases of * Drupal. See hook_update_last_removed() to notify Drupal about the removals. + * For further information about releases and release numbers see: + * @link http://drupal.org/node/711070 Maintaining a drupal.org project with Git @endlink * * Never renumber update functions. * - * Further information about releases and release numbers: - * - @link http://drupal.org/handbook/version-info http://drupal.org/handbook/version-info @endlink - * - @link http://drupal.org/node/93999 http://drupal.org/node/93999 @endlink (Overview of contributions branches and tags) - * - @link http://drupal.org/handbook/cvs/releases http://drupal.org/handbook/cvs/releases @endlink - * * Implementations of this hook should be placed in a mymodule.install file in * the same directory as mymodule.module. Drupal core's updates are implemented * using the system module as a name and stored in database/updates.inc. @@ -4044,7 +4112,7 @@ function hook_url_inbound_alter(&$path, $original_path, $path_language) { * @param $path * The outbound path to alter, not adjusted for path aliases yet. It won't be * adjusted for path aliases until all modules are finished altering it, thus - * being consistent with hook_url_alter_inbound(), which adjusts for all path + * being consistent with hook_url_inbound_alter(), which adjusts for all path * aliases before allowing modules to alter it. This may have been altered by * other modules before this one. * @param $options diff --git a/modules/system/system.info b/modules/system/system.info index 6fb99b689268598aca126df116cd69683ce32c60..b119521e2ff4bd287a35b1f56323a9ed1e33168a 100644 --- a/modules/system/system.info +++ b/modules/system/system.info @@ -12,8 +12,8 @@ files[] = system.test required = TRUE configure = admin/config/system -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/system/system.install b/modules/system/system.install index 95ee8c5de6173ae2a35234d637f651b0387e452b..aed7cc4890765f70ff567a5c9fc55f1a4c8de717 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -2242,10 +2242,6 @@ function system_update_7036() { )); } $insert->execute(); - - // Remove obsolete variable 'site_offline_message'. See - // update_fix_d7_requirements(). - variable_del('site_offline_message'); } /** @@ -2973,6 +2969,15 @@ function system_update_7071() { )); } +/** + * Remove the obsolete 'site_offline_message' variable. + * + * @see update_fix_d7_requirements() + */ +function system_update_7072() { + variable_del('site_offline_message'); +} + /** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/system/system.mail.inc b/modules/system/system.mail.inc index ef50642c55a9db8c36ceb9a976f5f0d78096a3fc..4e75440069b8bf8c01f3a59ee745db7979177a72 100644 --- a/modules/system/system.mail.inc +++ b/modules/system/system.mail.inc @@ -65,28 +65,49 @@ class DefaultMailSystem implements MailSystemInterface { // For headers, PHP's API suggests that we use CRLF normally, // but some MTAs incorrectly replace LF with CRLF. See #234403. $mail_headers = join("\n", $mimeheaders); - if (isset($message['Return-Path']) && !ini_get('safe_mode')) { - $mail_result = mail( - $message['to'], - $mail_subject, - $mail_body, - $mail_headers, - // Pass the Return-Path via sendmail's -f command. - '-f ' . $message['Return-Path'] - ); - } - else { - // The optional $additional_parameters argument to mail() is not allowed - // if safe_mode is enabled. Passing any value throws a PHP warning and - // makes mail() return FALSE. - $mail_result = mail( - $message['to'], - $mail_subject, - $mail_body, - $mail_headers - ); - } - return $mail_result; + + // We suppress warnings and notices from mail() because of issues on some + // hosts. The return value of this method will still indicate whether mail + // was sent successfully. + if (!isset($_SERVER['WINDIR']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') === FALSE) { + if (isset($message['Return-Path']) && !ini_get('safe_mode')) { + // On most non-Windows systems, the "-f" option to the sendmail command + // is used to set the Return-Path. There is no space between -f and + // the value of the return path. + $mail_result = @mail( + $message['to'], + $mail_subject, + $mail_body, + $mail_headers, + '-f' . $message['Return-Path'] + ); + } + else { + // The optional $additional_parameters argument to mail() is not + // allowed if safe_mode is enabled. Passing any value throws a PHP + // warning and makes mail() return FALSE. + $mail_result = @mail( + $message['to'], + $mail_subject, + $mail_body, + $mail_headers + ); + } + } + else { + // On Windows, PHP will use the value of sendmail_from for the + // Return-Path header. + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $message['Return-Path']); + $mail_result = @mail( + $message['to'], + $mail_subject, + $mail_body, + $mail_headers + ); + ini_set('sendmail_from', $old_from); + } + return $mail_result; } } diff --git a/modules/system/system.module b/modules/system/system.module index d0a542efb0eb428f85f865efe8dcf2c6d06f48f8..086a298e9ab9bdb7c87d8cdbf2de2bf81c941723 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -45,6 +45,13 @@ define('DRUPAL_OPTIONAL', 1); */ define('DRUPAL_REQUIRED', 2); +/** + * Maximum number of values in a weight select element. + * + * If the number of values is over the maximum, a text field is used instead. + */ +define('DRUPAL_WEIGHT_SELECT_MAX', 100); + /** * Return only visible regions. * @@ -969,7 +976,7 @@ function system_menu() { ); $items['admin/config/system/site-information'] = array( 'title' => 'Site information', - 'description' => t('Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.'), + 'description' => 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.', 'page callback' => 'drupal_get_form', 'page arguments' => array('system_site_information_settings'), 'access arguments' => array('administer site configuration'), @@ -977,8 +984,8 @@ function system_menu() { 'weight' => -20, ); $items['admin/config/system/cron'] = array( - 'title' => t('Cron'), - 'description' => t('Manage automatic site maintenance tasks.'), + 'title' => 'Cron', + 'description' => 'Manage automatic site maintenance tasks.', 'page callback' => 'drupal_get_form', 'page arguments' => array('system_cron_settings'), 'access arguments' => array('administer site configuration'), @@ -1108,7 +1115,7 @@ function system_library() { 'title' => 'Drupal progress indicator', 'version' => VERSION, 'js' => array( - 'misc/progress.js' => array('group' => JS_DEFAULT, 'cache' => FALSE), + 'misc/progress.js' => array('group' => JS_DEFAULT), ), ); @@ -2465,6 +2472,18 @@ function _system_update_bootstrap_status() { function _system_rebuild_theme_data() { // Find themes $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes'); + // Allow modules to add further themes. + if ($module_themes = module_invoke_all('system_theme_info')) { + foreach ($module_themes as $name => $uri) { + // @see file_scan_directory() + $themes[$name] = (object) array( + 'uri' => $uri, + 'filename' => pathinfo($uri, PATHINFO_FILENAME), + 'name' => $name, + ); + } + } + // Find theme engines $engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines'); @@ -2700,8 +2719,8 @@ function system_region_list($theme_key, $show = REGIONS_ALL) { * Implements hook_system_info_alter(). */ function system_system_info_alter(&$info, $file, $type) { - // Remove page-top from the blocks UI since it is reserved for modules to - // populate from outside the blocks system. + // Remove page-top and page-bottom from the blocks UI since they are reserved for + // modules to populate from outside the blocks system. if ($type == 'theme') { $info['regions_hidden'][] = 'page_top'; $info['regions_hidden'][] = 'page_bottom'; @@ -2864,7 +2883,18 @@ function confirm_form($form, $question, $path, $description = NULL, $yes = NULL, } /** - * Determines if the current user is in compact mode. + * Determines whether the current user is in compact mode. + * + * Compact mode shows certain administration pages with less description text, + * such as the configuration page and the permissions page. + * + * Whether the user is in compact mode is determined by a cookie, which is set + * for the user by system_admin_compact_page(). + * + * If the user does not have the cookie, the default value is given by the + * system variable 'admin_compact_mode', which itself defaults to FALSE. This + * does not have a user interface to set it: it is a hidden variable which can + * be set in the settings.php file. * * @return * TRUE when in compact mode, FALSE when in expanded mode. @@ -3420,12 +3450,12 @@ function system_image_toolkits() { function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $replace = FILE_EXISTS_RENAME) { $parsed_url = parse_url($url); if (!isset($destination)) { - $path = file_build_uri(basename($parsed_url['path'])); + $path = file_build_uri(drupal_basename($parsed_url['path'])); } else { if (is_dir(drupal_realpath($destination))) { // Prevent URIs with triple slashes when glueing parts together. - $path = str_replace('///', '//', "$destination/") . basename($parsed_url['path']); + $path = str_replace('///', '//', "$destination/") . drupal_basename($parsed_url['path']); } else { $path = $destination; @@ -3860,7 +3890,10 @@ function system_date_format_save($date_format, $dfid = 0) { drupal_write_record('date_formats', $info, $keys); } + // Retrieve an array of language objects for enabled languages. $languages = language_list('enabled'); + // This list is keyed off the value of $language->enabled; we want the ones + // that are enabled (value of 1). $languages = $languages[1]; $locale_format = array(); diff --git a/modules/system/system.queue.inc b/modules/system/system.queue.inc index 806015c242b6b7c11cd5bd776cd1912ecae11022..47d8e7cc3679c836852a8d317f4feb4eeb6a14c1 100644 --- a/modules/system/system.queue.inc +++ b/modules/system/system.queue.inc @@ -45,13 +45,13 @@ * would be an in-memory queue backend which might lose items if it crashes. * However, such a backend would be able to deal with significantly more writes * than a reliable queue and for many tasks this is more important. See - * aggregator_cron() for an example of how can this not be a problem. Another - * example is doing Twitter statistics -- the small possibility of losing a few - * items is insignificant next to power of the queue being able to keep up with - * writes. As described in the processing section, regardless of the queue - * being reliable or not, the processing code should be aware that an item - * might be handed over for processing more than once (because the processing - * code might time out before it finishes). + * aggregator_cron() for an example of how to effectively utilize a + * non-reliable queue. Another example is doing Twitter statistics -- the small + * possibility of losing a few items is insignificant next to power of the + * queue being able to keep up with writes. As described in the processing + * section, regardless of the queue being reliable or not, the processing code + * should be aware that an item might be handed over for processing more than + * once (because the processing code might time out before it finishes). */ /** diff --git a/modules/system/system.test b/modules/system/system.test index 8b305bc6da67bd2bb3a14bdd9a669ff2b8385a58..f40bd686ac0c192b99847222468c36ab7350baba 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -272,12 +272,32 @@ class EnableDisableTestCase extends ModuleTestCase { } /** - * Tests entity cache after enabling a module with a dependency on an enitity - * providing module. + * Ensures entity info cache is updated after changes. + */ + function testEntityInfoChanges() { + module_enable(array('entity_cache_test')); + $entity_info = entity_get_info(); + $this->assertTrue(isset($entity_info['entity_cache_test']), 'Test entity type found.'); + + // Change the label of the test entity type and make sure changes appear + // after flushing caches. + variable_set('entity_cache_test_label', 'New label.'); + drupal_flush_all_caches(); + $info = entity_get_info('entity_cache_test'); + $this->assertEqual($info['label'], 'New label.', 'New label appears in entity info.'); + + // Disable the providing module and make sure the entity type is gone. + module_disable(array('entity_cache_test', 'entity_cache_test_dependency')); + $entity_info = entity_get_info(); + $this->assertFalse(isset($entity_info['entity_cache_test']), 'Entity type of the providing module is gone.'); + } + + /** + * Tests entity info cache after enabling a module with a dependency on an entity providing module. * * @see entity_cache_test_watchdog() */ - function testEntityCache() { + function testEntityInfoCacheWatchdog() { module_enable(array('entity_cache_test')); $info = variable_get('entity_cache_test'); $this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.'); @@ -1861,6 +1881,9 @@ class TokenReplaceTestCase extends DrupalWebTestCase { $generated = token_generate('node', $raw_tokens, array('node' => $node), array('sanitize' => FALSE)); $this->assertEqual($generated['[node:title]'], $node->title, t('Unsanitized token generated properly.')); + + // Test token replacement when the string contains no tokens. + $this->assertEqual(token_replace('No tokens here.'), 'No tokens here.'); } /** @@ -2274,7 +2297,7 @@ class RetrieveFileTestCase extends DrupalWebTestCase { function testFileRetrieving() { // Test 404 handling by trying to fetch a randomly named file. drupal_mkdir($sourcedir = 'public://' . $this->randomName()); - $filename = $this->randomName(); + $filename = 'Файл для тестирования ' . $this->randomName(); $url = file_create_url($sourcedir . '/' . $filename); $retrieved_file = system_retrieve_file($url); $this->assertFalse($retrieved_file, t('Non-existent file not fetched.')); @@ -2282,7 +2305,12 @@ class RetrieveFileTestCase extends DrupalWebTestCase { // Actually create that file, download it via HTTP and test the returned path. file_put_contents($sourcedir . '/' . $filename, 'testing'); $retrieved_file = system_retrieve_file($url); - $this->assertEqual($retrieved_file, 'public://' . $filename, t('Sane path for downloaded file returned (public:// scheme).')); + + // URLs could not contains characters outside the ASCII set so $filename + // has to be encoded. + $encoded_filename = rawurlencode($filename); + + $this->assertEqual($retrieved_file, 'public://' . $encoded_filename, t('Sane path for downloaded file returned (public:// scheme).')); $this->assertTrue(is_file($retrieved_file), t('Downloaded file does exist (public:// scheme).')); $this->assertEqual(filesize($retrieved_file), 7, t('File size of downloaded file is correct (public:// scheme).')); file_unmanaged_delete($retrieved_file); @@ -2290,7 +2318,7 @@ class RetrieveFileTestCase extends DrupalWebTestCase { // Test downloading file to a different location. drupal_mkdir($targetdir = 'temporary://' . $this->randomName()); $retrieved_file = system_retrieve_file($url, $targetdir); - $this->assertEqual($retrieved_file, "$targetdir/$filename", t('Sane path for downloaded file returned (temporary:// scheme).')); + $this->assertEqual($retrieved_file, "$targetdir/$encoded_filename", t('Sane path for downloaded file returned (temporary:// scheme).')); $this->assertTrue(is_file($retrieved_file), t('Downloaded file does exist (temporary:// scheme).')); $this->assertEqual(filesize($retrieved_file), 7, t('File size of downloaded file is correct (temporary:// scheme).')); file_unmanaged_delete($retrieved_file); diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info index b7c13d6a348b30a0da941a200a927da79bdc82b0..206f55151e789f1e4e2cac4ee0e4bbae3cdda0db 100644 --- a/modules/taxonomy/taxonomy.info +++ b/modules/taxonomy/taxonomy.info @@ -8,8 +8,8 @@ files[] = taxonomy.module files[] = taxonomy.test configure = admin/structure/taxonomy -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 379de71c7cf6e185af1db4f0de5e1535f5da144e..ccbd7c5233484ec78de8fd93d3ce241b18de0389 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -295,7 +295,9 @@ function taxonomy_menu() { $items['taxonomy/term/%taxonomy_term/edit'] = array( 'title' => 'Edit', 'page callback' => 'drupal_get_form', - 'page arguments' => array('taxonomy_form_term', 2), + // Pass a NULL argument to ensure that additional path components are not + // passed to taxonomy_form_term() as the vocabulary machine name argument. + 'page arguments' => array('taxonomy_form_term', 2, NULL), 'access callback' => 'taxonomy_term_edit_access', 'access arguments' => array(2), 'type' => MENU_LOCAL_TASK, @@ -426,6 +428,7 @@ function taxonomy_vocabulary_save($vocabulary) { if (!empty($vocabulary->vid) && !empty($vocabulary->name)) { $status = drupal_write_record('taxonomy_vocabulary', $vocabulary, 'vid'); + taxonomy_vocabulary_static_reset(array($vocabulary->vid)); if ($vocabulary->old_machine_name != $vocabulary->machine_name) { field_attach_rename_bundle('taxonomy_term', $vocabulary->old_machine_name, $vocabulary->machine_name); } @@ -434,6 +437,7 @@ function taxonomy_vocabulary_save($vocabulary) { } elseif (empty($vocabulary->vid)) { $status = drupal_write_record('taxonomy_vocabulary', $vocabulary); + taxonomy_vocabulary_static_reset(); field_attach_create_bundle('taxonomy_term', $vocabulary->machine_name); module_invoke_all('taxonomy_vocabulary_insert', $vocabulary); module_invoke_all('entity_insert', $vocabulary, 'taxonomy_vocabulary'); @@ -441,7 +445,6 @@ function taxonomy_vocabulary_save($vocabulary) { unset($vocabulary->original); cache_clear_all(); - taxonomy_vocabulary_static_reset(array($vocabulary->vid)); return $status; } @@ -1023,9 +1026,9 @@ function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities break; } $term = $load_entities ? $term_entities[$child] : $terms[$vid][$child]; - if (count($parents[$vid][$term->tid]) > 1) { - // We have a term with multi parents here. Clone the term, - // so that the depth attribute remains correct. + if (isset($parents[$vid][$term->tid])) { + // Clone the term so that the depth attribute remains correct + // in the event of multiple parents. $term = clone $term; } $term->depth = $depth; @@ -1194,6 +1197,8 @@ function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array( * @return * The vocabulary object with all of its metadata, if exists, FALSE otherwise. * Results are statically cached. + * + * @see taxonomy_vocabulary_machine_name_load() */ function taxonomy_vocabulary_load($vid) { $vocabularies = taxonomy_vocabulary_load_multiple(array($vid)); @@ -1209,6 +1214,8 @@ function taxonomy_vocabulary_load($vid) { * @return * The vocabulary object with all of its metadata, if exists, FALSE otherwise. * Results are statically cached. + * + * @see taxonomy_vocabulary_load() */ function taxonomy_vocabulary_machine_name_load($name) { $vocabularies = taxonomy_vocabulary_load_multiple(NULL, array('machine_name' => $name)); @@ -1724,48 +1731,75 @@ function taxonomy_field_presave($entity_type, $entity, $field, $instance, $langc } /** - * Implements hook_field_insert(). + * Implements hook_node_insert(). */ -function taxonomy_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { - // We maintain a denormalized table of term/node relationships, containing - // only data for current, published nodes. - if (variable_get('taxonomy_maintain_index_table', TRUE) && $field['storage']['type'] == 'field_sql_storage' && $entity_type == 'node' && $entity->status) { - $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created', )); - foreach ($items as $item) { - $query->values(array( - 'nid' => $entity->nid, - 'tid' => $item['tid'], - 'sticky' => $entity->sticky, - 'created' => $entity->created, - )); - } - $query->execute(); - } +function taxonomy_node_insert($node) { + // Add taxonomy index entries for the node. + taxonomy_build_node_index($node); } /** - * Implements hook_field_update(). + * Builds and inserts taxonomy index entries for a given node. + * + * The index lists all terms that are related to a given node entity, and is + * therefore maintained at the entity level. + * + * @param $node + * The node object. */ -function taxonomy_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { - if (variable_get('taxonomy_maintain_index_table', TRUE) && $field['storage']['type'] == 'field_sql_storage' && $entity_type == 'node') { - $first_call = &drupal_static(__FUNCTION__, array()); - - // We don't maintain data for old revisions, so clear all previous values - // from the table. Since this hook runs once per field, per object, make - // sure we only wipe values once. - if (!isset($first_call[$entity->nid])) { - $first_call[$entity->nid] = FALSE; - db_delete('taxonomy_index')->condition('nid', $entity->nid)->execute(); +function taxonomy_build_node_index($node) { + // We maintain a denormalized table of term/node relationships, containing + // only data for current, published nodes. + $status = NULL; + if (variable_get('taxonomy_maintain_index_table', TRUE)) { + // If a node property is not set in the node object when node_save() is + // called, the old value from $node->original is used. + if (!empty($node->original)) { + $status = (int)(!empty($node->status) || (!isset($node->status) && !empty($node->original->status))); + $sticky = (int)(!empty($node->sticky) || (!isset($node->sticky) && !empty($node->original->sticky))); + } + else { + $status = (int)(!empty($node->status)); + $sticky = (int)(!empty($node->sticky)); + } + } + // We only maintain the taxonomy index for published nodes. + if ($status) { + // Collect a unique list of all the term IDs from all node fields. + $tid_all = array(); + foreach (field_info_instances('node', $node->type) as $instance) { + $field_name = $instance['field_name']; + $field = field_info_field($field_name); + if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') { + // If a field value is not set in the node object when node_save() is + // called, the old value from $node->original is used. + if (isset($node->{$field_name})) { + $items = $node->{$field_name}; + } + elseif (isset($node->original->{$field_name})) { + $items = $node->original->{$field_name}; + } + else { + continue; + } + foreach (field_available_languages('node', $field) as $langcode) { + if (!empty($items[$langcode])) { + foreach ($items[$langcode] as $item) { + $tid_all[$item['tid']] = $item['tid']; + } + } + } + } } - // Only save data to the table if the node is published. - if ($entity->status) { + // Insert index entries for all the node's terms. + if (!empty($tid_all)) { $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created')); - foreach ($items as $item) { + foreach ($tid_all as $tid) { $query->values(array( - 'nid' => $entity->nid, - 'tid' => $item['tid'], - 'sticky' => $entity->sticky, - 'created' => $entity->created, + 'nid' => $node->nid, + 'tid' => $tid, + 'sticky' => $sticky, + 'created' => $node->created, )); } $query->execute(); @@ -1773,12 +1807,31 @@ function taxonomy_field_update($entity_type, $entity, $field, $instance, $langco } } +/** + * Implements hook_node_update(). + */ +function taxonomy_node_update($node) { + // Always rebuild the node's taxonomy index entries on node save. + taxonomy_delete_node_index($node); + taxonomy_build_node_index($node); +} + /** * Implements hook_node_delete(). */ function taxonomy_node_delete($node) { + // Clean up the {taxonomy_index} table when nodes are deleted. + taxonomy_delete_node_index($node); +} + +/** + * Deletes taxonomy index entries for a given node. + * + * @param $node + * The node object. + */ +function taxonomy_delete_node_index($node) { if (variable_get('taxonomy_maintain_index_table', TRUE)) { - // Clean up the {taxonomy_index} table when nodes are deleted. db_delete('taxonomy_index')->condition('nid', $node->nid)->execute(); } } diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test index 9a89b9c98ff63924d7d592c7408efe70cdb756d4..25743bfea9a796efafa056903f63fb2e06b79038 100644 --- a/modules/taxonomy/taxonomy.test +++ b/modules/taxonomy/taxonomy.test @@ -410,6 +410,60 @@ class TaxonomyTermUnitTest extends TaxonomyWebTestCase { // Delete an invalid term. Should not throw any notices. taxonomy_term_delete(42); } + + /** + * Test a taxonomy with terms that have multiple parents of different depths. + */ + function testTaxonomyVocabularyTree() { + // Create a new vocabulary with 6 terms. + $vocabulary = $this->createVocabulary(); + $term = array(); + for ($i = 0; $i < 6; $i++) { + $term[$i] = $this->createTerm($vocabulary); + } + + // $term[2] is a child of 1 and 5. + $term[2]->parent = array($term[1]->tid, $term[5]->tid); + taxonomy_term_save($term[2]); + // $term[3] is a child of 2. + $term[3]->parent = array($term[2]->tid); + taxonomy_term_save($term[3]); + // $term[5] is a child of 4. + $term[5]->parent = array($term[4]->tid); + taxonomy_term_save($term[5]); + + /** + * Expected tree: + * term[0] | depth: 0 + * term[1] | depth: 0 + * -- term[2] | depth: 1 + * ---- term[3] | depth: 2 + * term[4] | depth: 0 + * -- term[5] | depth: 1 + * ---- term[2] | depth: 2 + * ------ term[3] | depth: 3 + */ + + // Count $term[1] parents with $max_depth = 1. + $tree = taxonomy_get_tree($vocabulary->vid, $term[1]->tid, 1); + $this->assertEqual(1, count($tree), 'We have one parent with depth 1.'); + + // Count all vocabulary tree elements. + $tree = taxonomy_get_tree($vocabulary->vid); + $this->assertEqual(8, count($tree), 'We have all vocabulary tree elements.'); + + // Count elements in every tree depth. + foreach($tree as $element) { + if (!isset($depth_count[$element->depth])) { + $depth_count[$element->depth] = 0; + } + $depth_count[$element->depth]++; + } + $this->assertEqual(3, $depth_count[0], 'Three elements in taxonomy tree depth 0.'); + $this->assertEqual(2, $depth_count[1], 'Two elements in taxonomy tree depth 1.'); + $this->assertEqual(2, $depth_count[2], 'Two elements in taxonomy tree depth 2.'); + $this->assertEqual(1, $depth_count[3], 'One element in taxonomy tree depth 3.'); + } } /** @@ -579,9 +633,9 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $instance['bundle'] = 'page'; field_create_instance($instance); $terms = array( - $this->randomName(), - $this->randomName() . ', ' . $this->randomName(), - $this->randomName(), + 'term1' => $this->randomName(), + 'term2' => $this->randomName() . ', ' . $this->randomName(), + 'term3' => $this->randomName(), ); $edit = array(); @@ -611,33 +665,37 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { } // Get the created terms. - list($term1, $term2, $term3) = array_values(taxonomy_term_load_multiple(FALSE)); + $term_objects = array(); + foreach ($terms as $key => $term) { + $term_objects[$key] = taxonomy_get_term_by_name($term); + $term_objects[$key] = reset($term_objects[$key]); + } // Delete term 1. - $this->drupalPost('taxonomy/term/' . $term1->tid . '/edit', array(), t('Delete')); + $this->drupalPost('taxonomy/term/' . $term_objects['term1']->tid . '/edit', array(), t('Delete')); $this->drupalPost(NULL, NULL, t('Delete')); - $term_names = array($term2->name, $term3->name); + $term_names = array($term_objects['term2']->name, $term_objects['term3']->name); // Get the node. $node = $this->drupalGetNodeByTitle($edit["title"]); $this->drupalGet('node/' . $node->nid); foreach ($term_names as $term_name) { - $this->assertText($term_name, t('The term %name appears on the node page after one term %deleted was deleted', array('%name' => $term_name, '%deleted' => $term1->name))); + $this->assertText($term_name, t('The term %name appears on the node page after one term %deleted was deleted', array('%name' => $term_name, '%deleted' => $term_objects['term1']->name))); } - $this->assertNoText($term1->name, t('The deleted term %name does not appear on the node page.', array('%name' => $term1->name))); + $this->assertNoText($term_objects['term1']->name, t('The deleted term %name does not appear on the node page.', array('%name' => $term_objects['term1']->name))); - // Test autocomplete on term 2 - it contains a comma, so expect the key to - // be quoted. - $input = substr($term2->name, 0, 3); + // Test autocomplete on term 2, which contains a comma. + // The term will be quoted, and the " will be encoded in unicode (\u0022). + $input = substr($term_objects['term2']->name, 0, 3); $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input); - $this->assertRaw('{"\"' . $term2->name . '\"":"' . $term2->name . '"}', t('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term2->name))); + $this->assertRaw('{"\u0022' . $term_objects['term2']->name . '\u0022":"' . $term_objects['term2']->name . '"}', t('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term2']->name))); // Test autocomplete on term 3 - it is alphanumeric only, so no extra // quoting. - $input = substr($term3->name, 0, 3); + $input = substr($term_objects['term3']->name, 0, 3); $this->drupalGet('taxonomy/autocomplete/taxonomy_' . $this->vocabulary->machine_name . '/' . $input); - $this->assertRaw('{"' . $term3->name . '":"' . $term3->name . '"}', t('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term3->name))); + $this->assertRaw('{"' . $term_objects['term3']->name . '":"' . $term_objects['term3']->name . '"}', t('Autocomplete returns term %term_name after typing the first 3 letters.', array('%term_name' => $term_objects['term3']->name))); } /** @@ -699,6 +757,10 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { // Check that the term feed page is working. $this->drupalGet('taxonomy/term/' . $term->tid . '/feed'); + // Check that the term edit page does not try to interpret additional path + // components as arguments for taxonomy_form_term(). + $this->drupalGet('taxonomy/term/' . $term->tid . '/edit/' . $this->randomName()); + // Delete the term. $this->drupalPost('taxonomy/term/' . $term->tid . '/edit', array(), t('Delete')); $this->drupalPost(NULL, NULL, t('Delete')); @@ -828,6 +890,223 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { } } +/** + * Tests the hook implementations that maintain the taxonomy index. + */ +class TaxonomyTermIndexTestCase extends TaxonomyWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Taxonomy term index', + 'description' => 'Tests the hook implementations that maintain the taxonomy index.', + 'group' => 'Taxonomy', + ); + } + + function setUp() { + parent::setUp('taxonomy'); + + // Create an administrative user. + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access')); + $this->drupalLogin($this->admin_user); + + // Create a vocabulary and add two term reference fields to article nodes. + $this->vocabulary = $this->createVocabulary(); + + $this->field_name_1 = drupal_strtolower($this->randomName()); + $this->field_1 = array( + 'field_name' => $this->field_name_1, + 'type' => 'taxonomy_term_reference', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $this->vocabulary->machine_name, + 'parent' => 0, + ), + ), + ), + ); + field_create_field($this->field_1); + $this->instance_1 = array( + 'field_name' => $this->field_name_1, + 'bundle' => 'article', + 'entity_type' => 'node', + 'widget' => array( + 'type' => 'options_select', + ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + ), + ), + ); + field_create_instance($this->instance_1); + + $this->field_name_2 = drupal_strtolower($this->randomName()); + $this->field_2 = array( + 'field_name' => $this->field_name_2, + 'type' => 'taxonomy_term_reference', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $this->vocabulary->machine_name, + 'parent' => 0, + ), + ), + ), + ); + field_create_field($this->field_2); + $this->instance_2 = array( + 'field_name' => $this->field_name_2, + 'bundle' => 'article', + 'entity_type' => 'node', + 'widget' => array( + 'type' => 'options_select', + ), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + ), + ), + ); + field_create_instance($this->instance_2); + } + + /** + * Tests that the taxonomy index is maintained properly. + */ + function testTaxonomyIndex() { + // Create terms in the vocabulary. + $term_1 = $this->createTerm($this->vocabulary); + $term_2 = $this->createTerm($this->vocabulary); + + // Post an article. + $edit = array(); + $langcode = LANGUAGE_NONE; + $edit["title"] = $this->randomName(); + $edit["body[$langcode][0][value]"] = $this->randomName(); + $edit["{$this->field_name_1}[$langcode][]"] = $term_1->tid; + $edit["{$this->field_name_2}[$langcode][]"] = $term_1->tid; + $this->drupalPost('node/add/article', $edit, t('Save')); + + // Check that the term is indexed, and only once. + $node = $this->drupalGetNodeByTitle($edit["title"]); + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_1->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 1 is indexed once.')); + + // Update the article to change one term. + $edit["{$this->field_name_1}[$langcode][]"] = $term_2->tid; + $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); + + // Check that both terms are indexed. + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_1->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 1 is indexed.')); + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_2->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 2 is indexed.')); + + // Update the article to change another term. + $edit["{$this->field_name_2}[$langcode][]"] = $term_2->tid; + $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); + + // Check that only one term is indexed. + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_1->tid, + ))->fetchField(); + $this->assertEqual(0, $index_count, t('Term 1 is not indexed.')); + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_2->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 2 is indexed once.')); + + // Redo the above tests without interface. + $update_node = array( + 'nid' => $node->nid, + 'vid' => $node->vid, + 'uid' => $node->uid, + 'type' => $node->type, + 'title' => $this->randomName(), + ); + + // Update the article with no term changed. + $updated_node = (object) $update_node; + node_save($updated_node); + + // Check that the index was not changed. + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_1->tid, + ))->fetchField(); + $this->assertEqual(0, $index_count, t('Term 1 is not indexed.')); + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_2->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 2 is indexed once.')); + + // Update the article to change one term. + $update_node[$this->field_name_1][$langcode] = array(array('tid' => $term_1->tid)); + $updated_node = (object) $update_node; + node_save($updated_node); + + // Check that both terms are indexed. + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_1->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 1 is indexed.')); + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_2->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 2 is indexed.')); + + // Update the article to change another term. + $update_node[$this->field_name_2][$langcode] = array(array('tid' => $term_1->tid)); + $updated_node = (object) $update_node; + node_save($updated_node); + + // Check that only one term is indexed. + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_1->tid, + ))->fetchField(); + $this->assertEqual(1, $index_count, t('Term 1 is indexed once.')); + $index_count = db_query('SELECT COUNT(*) FROM {taxonomy_index} WHERE nid = :nid AND tid = :tid', array( + ':nid' => $node->nid, + ':tid' => $term_2->tid, + ))->fetchField(); + $this->assertEqual(0, $index_count, t('Term 2 is not indexed.')); + } + + /** + * Tests that there is a link to the parent term on the child term page. + */ + function testTaxonomyTermHierarchyBreadcrumbs() { + // Create two taxonomy terms and set term2 as the parent of term1. + $term1 = $this->createTerm($this->vocabulary); + $term2 = $this->createTerm($this->vocabulary); + $term1->parent = array($term2->tid); + taxonomy_term_save($term1); + + // Verify that the page breadcrumbs include a link to the parent term. + $this->drupalGet('taxonomy/term/' . $term1->tid); + $this->assertRaw(l($term2->name, 'taxonomy/term/' . $term2->tid), t('Parent term link is displayed when viewing the node.')); + } +} + /** * Test the taxonomy_term_load_multiple() function. */ @@ -1080,10 +1359,16 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { ); field_update_field($this->field); // Change the machine name. + $old_name = $this->vocabulary->machine_name; $new_name = drupal_strtolower($this->randomName()); $this->vocabulary->machine_name = $new_name; taxonomy_vocabulary_save($this->vocabulary); + // Check that entity bundles are properly updated. + $info = entity_get_info('taxonomy_term'); + $this->assertFalse(isset($info['bundles'][$old_name]), t('The old bundle name does not appear in entity_get_info().')); + $this->assertTrue(isset($info['bundles'][$new_name]), t('The new bundle name appears in entity_get_info().')); + // Check that the field instance is still attached to the vocabulary. $field = field_info_field($this->field_name); $allowed_values = $field['settings']['allowed_values']; diff --git a/modules/toolbar/toolbar.info b/modules/toolbar/toolbar.info index adc9eba5f5c88bc2bbb305bacd0f58ff5a2707df..fd1d6a1a7303520c0461a35b6d19087a8572ae37 100644 --- a/modules/toolbar/toolbar.info +++ b/modules/toolbar/toolbar.info @@ -4,8 +4,8 @@ core = 7.x package = Core version = VERSION -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/toolbar/toolbar.module b/modules/toolbar/toolbar.module index 61ae648ad5f172d992c6c6edaedd1487138642e6..de2d2619b90db7c824f23d7bee774284edc1a6d0 100644 --- a/modules/toolbar/toolbar.module +++ b/modules/toolbar/toolbar.module @@ -82,6 +82,7 @@ function toolbar_toggle_page() { * An associative array containing: * - collapsed: A boolean value representing the toolbar drawer's visibility. * - attributes: An associative array of HTML attributes. + * * @return * An HTML string representing the element for toggling. * @@ -175,7 +176,10 @@ function toolbar_system_info_alter(&$info, $file, $type) { } /** - * Build the admin menu as a structured array ready for drupal_render(). + * Builds the admin menu as a structured array ready for drupal_render(). + * + * @return + * Array of links and settings relating to the admin menu. */ function toolbar_view() { global $user; @@ -272,7 +276,10 @@ function toolbar_view() { } /** - * Get only the top level items below the 'admin' path. + * Gets only the top level items below the 'admin' path. + * + * @return + * An array containing a menu tree of top level items below the 'admin' path. */ function toolbar_get_menu_tree() { $tree = array(); @@ -289,10 +296,13 @@ function toolbar_get_menu_tree() { } /** - * Generate a links array from a menu tree array. + * Generates a links array from a menu tree array. * * Based on menu_navigation_links(). Adds path based IDs and icon placeholders * to the links. + * + * @return + * An array of links as defined above. */ function toolbar_menu_navigation_links($tree) { $links = array(); @@ -330,6 +340,9 @@ function toolbar_menu_navigation_links($tree) { * Useful when using a menu generated by menu_tree_all_data() which does * not set the 'in_active_trail' flag on items. * + * @return + * TRUE when path is in the active trail, FALSE if not. + * * @todo * Look at migrating to a menu system level function. */ diff --git a/modules/tracker/tracker.info b/modules/tracker/tracker.info index a0d821d608bdd98367c1a825034eddc1d0a9a559..056b1215347767f98f9eb83766cbf3e0df202509 100644 --- a/modules/tracker/tracker.info +++ b/modules/tracker/tracker.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = tracker.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/tracker/tracker.test b/modules/tracker/tracker.test index 3cc227eae178dedba2d16e7f987e7d3121aa9ce5..a559f1b64be52004b0c12c55596f3bc419d60c83 100644 --- a/modules/tracker/tracker.test +++ b/modules/tracker/tracker.test @@ -8,7 +8,6 @@ class TrackerTest extends DrupalWebTestCase { protected $user; protected $other_user; - protected $new_node; public static function getInfo() { return array( diff --git a/modules/translation/tests/translation_test.info b/modules/translation/tests/translation_test.info index d252b74ca27ab169ac5bbb090c4649031bc2392f..3da9476d0f6665f669cc5f8644acc9db971e12ef 100644 --- a/modules/translation/tests/translation_test.info +++ b/modules/translation/tests/translation_test.info @@ -5,8 +5,8 @@ package = Testing version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/translation/translation.info b/modules/translation/translation.info index 0c850e85750f0cf34943259a9fbc162f8f887cb5..3040c76ad90248982fc226c3058f861f9a9720f7 100644 --- a/modules/translation/translation.info +++ b/modules/translation/translation.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = translation.test -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/trigger/tests/trigger_test.info b/modules/trigger/tests/trigger_test.info index 3aea92f7555a3badff706be92a3bc8e0f80a59f7..6478e0788653f2883eee96cc4eda5dbdc340ca4b 100644 --- a/modules/trigger/tests/trigger_test.info +++ b/modules/trigger/tests/trigger_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/trigger/trigger.info b/modules/trigger/trigger.info index b2ee810a6bc9a19d6943c2d4bdcf8d556ebca20b..0990bc2b85d99ed5c82957ee764c3b9a35fed6d7 100644 --- a/modules/trigger/trigger.info +++ b/modules/trigger/trigger.info @@ -6,8 +6,8 @@ core = 7.x files[] = trigger.test configure = admin/structure/trigger -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/update/tests/aaa_update_test.info b/modules/update/tests/aaa_update_test.info index 755a4ee93fe5f29e077c640b5d4cae91c6885225..cfd1fec1c6833f810e3d027b99ec3846a36970d0 100644 --- a/modules/update/tests/aaa_update_test.info +++ b/modules/update/tests/aaa_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/update/tests/bbb_update_test.info b/modules/update/tests/bbb_update_test.info index 5c6227b27b3b0e0373139599faec9c0893329e6b..40ad68e4173e1fa8de15fd433062ff7c685a1af4 100644 --- a/modules/update/tests/bbb_update_test.info +++ b/modules/update/tests/bbb_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/update/tests/ccc_update_test.info b/modules/update/tests/ccc_update_test.info index 9b3312ddd68bac0c30b727cf27b76eb74d15f02f..b78a3b62a18395e40551afc7989e53677546faa7 100644 --- a/modules/update/tests/ccc_update_test.info +++ b/modules/update/tests/ccc_update_test.info @@ -4,8 +4,8 @@ package = Testing core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/tests/update_test_basetheme/update_test_basetheme.info b/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info similarity index 59% rename from themes/tests/update_test_basetheme/update_test_basetheme.info rename to modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info index a079e934790b8c00330c623d3810dda15401b514..f60c1400d4a8cc74d7636d80d51f3c40e9f2e764 100644 --- a/themes/tests/update_test_basetheme/update_test_basetheme.info +++ b/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info @@ -3,8 +3,8 @@ description = Test theme which acts as a base theme for other test subthemes. core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/tests/update_test_subtheme/update_test_subtheme.info b/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info similarity index 63% rename from themes/tests/update_test_subtheme/update_test_subtheme.info rename to modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info index c69ec81d16c84bd6a1d8f291dbf6f0cbffe65881..cc7cb539142d9e3baa1e6431a2ff6ee9b5801fa8 100644 --- a/themes/tests/update_test_subtheme/update_test_subtheme.info +++ b/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info @@ -4,8 +4,8 @@ core = 7.x base theme = update_test_basetheme hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/update/tests/update_test.info b/modules/update/tests/update_test.info index 7626a96c4e3918294549c05a6ec4f756dcc46682..1e58d53f3e4dabe201d5125f0e7e35cb6f306738 100644 --- a/modules/update/tests/update_test.info +++ b/modules/update/tests/update_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/update/tests/update_test.module b/modules/update/tests/update_test.module index 4acb6ef837e605af25927aa7aeda03fea82212de..e7ee43eecf2eb5bee9cfbee201675fb734d1067b 100644 --- a/modules/update/tests/update_test.module +++ b/modules/update/tests/update_test.module @@ -1,5 +1,14 @@ <?php +/** + * Implements hook_system_theme_info(). + */ +function update_test_system_theme_info() { + $themes['update_test_basetheme'] = drupal_get_path('module', 'update_test') . '/themes/update_test_basetheme/update_test_basetheme.info'; + $themes['update_test_subtheme'] = drupal_get_path('module', 'update_test') . '/themes/update_test_subtheme/update_test_subtheme.info'; + return $themes; +} + /** * Implements hook_menu(). */ diff --git a/modules/update/update.info b/modules/update/update.info index c3d5c3457a4a61c8faa56f91fc55946101cd85da..92d10d8875ac9a9c413933986b3ff3129c6a2656 100644 --- a/modules/update/update.info +++ b/modules/update/update.info @@ -6,8 +6,8 @@ core = 7.x files[] = update.test configure = admin/reports/updates/settings -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/update/update.manager.inc b/modules/update/update.manager.inc index 59858ebe810ea6411b15786111653737d08a8804..d9fd86ff29792b8db6d67752a2f27fdc21c01fc0 100644 --- a/modules/update/update.manager.inc +++ b/modules/update/update.manager.inc @@ -816,7 +816,7 @@ function update_manager_file_get($url) { // Check the cache and download the file if needed. $cache_directory = _update_manager_cache_directory(); - $local = $cache_directory . '/' . basename($parsed_url['path']); + $local = $cache_directory . '/' . drupal_basename($parsed_url['path']); if (!file_exists($local) || update_delete_file_if_stale($local)) { return system_retrieve_file($url, $local, FALSE, FILE_EXISTS_REPLACE); diff --git a/modules/update/update.module b/modules/update/update.module index a2d705a0eb02bc5eed090c5f254caa85b863b3a0..293a53d9060ce61ab5c077397079cac8c57b13a2 100644 --- a/modules/update/update.module +++ b/modules/update/update.module @@ -695,14 +695,14 @@ function update_verify_update_archive($project, $archive_file, $directory) { } if (empty($files)) { - $errors[] = t('%archive_file does not contain any .info files.', array('%archive_file' => basename($archive_file))); + $errors[] = t('%archive_file does not contain any .info files.', array('%archive_file' => drupal_basename($archive_file))); } elseif (!$compatible_project) { $errors[] = format_plural( count($incompatible), '%archive_file contains a version of %names that is not compatible with Drupal !version.', '%archive_file contains versions of modules or themes that are not compatible with Drupal !version: %names', - array('!version' => DRUPAL_CORE_COMPATIBILITY, '%archive_file' => basename($archive_file), '%names' => implode(', ', $incompatible)) + array('!version' => DRUPAL_CORE_COMPATIBILITY, '%archive_file' => drupal_basename($archive_file), '%names' => implode(', ', $incompatible)) ); } diff --git a/modules/user/tests/user_form_test.info b/modules/user/tests/user_form_test.info index 30d2f3f84d10ba67e3d7924b11fe439ccfe32e9a..50a5a26378912f47545d3382b8a2e513f7764d67 100644 --- a/modules/user/tests/user_form_test.info +++ b/modules/user/tests/user_form_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/user/user.info b/modules/user/user.info index 00b2db3eb4a0ad949e01cc14a89c50b157241c42..22a5154a3b6c952f27eafb489bdb744e7b759707 100644 --- a/modules/user/user.info +++ b/modules/user/user.info @@ -9,8 +9,8 @@ required = TRUE configure = admin/config/people stylesheets[all][] = user.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/modules/user/user.install b/modules/user/user.install index 9119aac079aa46cdcda868a6780e1494fced6f06..a48feb5f893fb01c7d87eb2d7ab491a56e18beed 100644 --- a/modules/user/user.install +++ b/modules/user/user.install @@ -743,7 +743,7 @@ function user_update_7012(&$sandbox) { // Create a file object. $file = new stdClass(); $file->uri = $user->picture; - $file->filename = basename($file->uri); + $file->filename = drupal_basename($file->uri); $file->filemime = file_get_mimetype($file->uri); $file->uid = $user->uid; $file->status = FILE_STATUS_PERMANENT; diff --git a/modules/user/user.js b/modules/user/user.js index 44c00f344a031f93ea77d29868a5aac60a941cfc..73af27e5d969a22dbbbae94436f890c07533f6af 100644 --- a/modules/user/user.js +++ b/modules/user/user.js @@ -168,7 +168,7 @@ Drupal.evaluatePasswordStrength = function (password, translate) { // Assemble the final message. msg = translate.hasWeaknesses + '<ul><li>' + msg.join('</li><li>') + '</li></ul>'; - return { strength: strength, message: msg, indicatorText: indicatorText } + return { strength: strength, message: msg, indicatorText: indicatorText }; }; diff --git a/modules/user/user.module b/modules/user/user.module index 48b17af907479782df64acac8b066833e45f7c69..da61f44d8f448c468d2363e6282f594dd3851a82 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -3466,23 +3466,27 @@ function user_preferred_language($account, $default = NULL) { * @see drupal_mail() * * @param $op - * The operation being performed on the account. Possible values: - * 'register_admin_created': Welcome message for user created by the admin - * 'register_no_approval_required': Welcome message when user self-registers - * 'register_pending_approval': Welcome message, user pending admin approval - * 'password_reset': Password recovery request - * 'status_activated': Account activated - * 'status_blocked': Account blocked - * 'cancel_confirm': Account cancellation request - * 'status_canceled': Account canceled + * The operation being performed on the account. Possible values: + * - 'register_admin_created': Welcome message for user created by the admin. + * - 'register_no_approval_required': Welcome message when user + * self-registers. + * - 'register_pending_approval': Welcome message, user pending admin + * approval. + * - 'password_reset': Password recovery request. + * - 'status_activated': Account activated. + * - 'status_blocked': Account blocked. + * - 'cancel_confirm': Account cancellation request. + * - 'status_canceled': Account canceled. * * @param $account - * The user object of the account being notified. Must contain at - * least the fields 'uid', 'name', and 'mail'. + * The user object of the account being notified. Must contain at + * least the fields 'uid', 'name', and 'mail'. * @param $language - * Optional language to use for the notification, overriding account language. + * Optional language to use for the notification, overriding account language. + * * @return - * The return value from drupal_mail_system()->mail(), if ends up being called. + * The return value from drupal_mail_system()->mail(), if ends up being + * called. */ function _user_mail_notify($op, $account, $language = NULL) { // By default, we always notify except for canceled and blocked. diff --git a/profiles/minimal/minimal.info b/profiles/minimal/minimal.info index e5d1cc53c4f44f1f48f8e4966cd85755496dd799..5464aa770c5eeb34e597e7ae8c65e6d88fc90960 100644 --- a/profiles/minimal/minimal.info +++ b/profiles/minimal/minimal.info @@ -6,8 +6,8 @@ dependencies[] = block dependencies[] = dblog files[] = minimal.profile -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/profiles/standard/standard.info b/profiles/standard/standard.info index 2ff24a7aa55a693bdf1e816b2bf4db9a5e6c1c32..bdb3d840c69955d0ce9406651a8f3e745f20a01a 100644 --- a/profiles/standard/standard.info +++ b/profiles/standard/standard.info @@ -25,8 +25,8 @@ dependencies[] = file dependencies[] = rdf files[] = standard.profile -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/profiles/standard/standard.install b/profiles/standard/standard.install index 5d447177fb26261c51fd7e3b3017fbd9ddc0ca46..ccc70443fa2efc97affeb343cef0e7ea0fe6782a 100644 --- a/profiles/standard/standard.install +++ b/profiles/standard/standard.install @@ -65,7 +65,7 @@ function standard_install() { // Enable some standard blocks. $default_theme = variable_get('theme_default', 'bartik'); $admin_theme = 'seven'; - $values = array( + $blocks = array( array( 'module' => 'system', 'delta' => 'main', @@ -188,8 +188,8 @@ function standard_install() { ), ); $query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache')); - foreach ($values as $record) { - $query->values($record); + foreach ($blocks as $block) { + $query->values($block); } $query->execute(); diff --git a/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info index 82b257c85d6c2ae168ba1064b87b5ab4d33776e9..82e993aa40fa9807cb0bbd81929fce98a99cc931 100644 --- a/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info +++ b/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info index 3c1a631bbab9c822a97512e7a8677cf66a88f104..e4b602f3fccc2b7d1820438a63860d418c3ff852 100644 --- a/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info +++ b/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info @@ -8,8 +8,8 @@ version = VERSION core = 6.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/profiles/testing/testing.info b/profiles/testing/testing.info index e4deb306a5ca2cf7d897a69c144ca11b5ce8cf96..a7925857ac8e9b76f572942ee76b37e758ab2abc 100644 --- a/profiles/testing/testing.info +++ b/profiles/testing/testing.info @@ -4,8 +4,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/scripts/generate-d7-content.sh b/scripts/generate-d7-content.sh index 2ad9e5286be6a95db073b2f9f86c975e38ac48a5..364a25c92c6be805dea61b17a163adc31e470317 100644 --- a/scripts/generate-d7-content.sh +++ b/scripts/generate-d7-content.sh @@ -269,11 +269,14 @@ if (module_exists('poll')) { } // Test that upgrade works even on a bundle whose parent module was disabled. +// This is simulated by creating an existing content type and changing the +// bundle to another type through direct database update queries. +$node_type = 'broken'; $uid = 6; $user = user_load($uid); $node = new stdClass(); $node->uid = $uid; -$node->type = 'broken'; +$node->type = 'article'; $body_text = str_repeat("node body ($node_type) - 37", 100); $node->sticky = 0; $node->title = "node title 24"; diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 429b28b2405f9d6f467941e34155287b7d35fcf6..b7d81830b29f4220af13ef6efed937803a984022 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -585,9 +585,8 @@ function simpletest_script_reporter_display_results() { if ($args['verbose']) { // Report results. - echo "Detailed test results:\n"; - echo "----------------------\n"; - echo "\n"; + echo "Detailed test results\n"; + echo "---------------------\n"; $results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id)); $test_class = ''; @@ -597,6 +596,10 @@ function simpletest_script_reporter_display_results() { // Display test class every time results are for new test class. echo "\n\n---- $result->test_class ----\n\n\n"; $test_class = $result->test_class; + + // Print table header. + echo "Status Group Filename Line Function \n"; + echo "--------------------------------------------------------------------------------\n"; } simpletest_script_format_result($result); @@ -614,8 +617,8 @@ function simpletest_script_reporter_display_results() { function simpletest_script_format_result($result) { global $results_map, $color; - $summary = sprintf("%-10.10s %-10.10s %-30.30s %-5.5s %-20.20s\n", - $results_map[$result->status], $result->message_group, basename($result->file), $result->line, $result->caller); + $summary = sprintf("%-9.9s %-10.10s %-17.17s %4.4s %-35.35s\n", + $results_map[$result->status], $result->message_group, basename($result->file), $result->line, $result->function); simpletest_script_print($summary, simpletest_script_color_code($result->status)); diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index f8894aaf5c69877ed0d2276af0d1fa0dcce26fa9..d8ab0e62f1154ba0a5ab3aa12fad913c8a4ec658 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -153,6 +153,29 @@ * @endcode * NOTE: MySQL and SQLite's definition of a schema is a database. * + * Advanced users can add or override initial commands to execute when + * connecting to the database server, as well as PDO connection settings. For + * example, to enable MySQL SELECT queries to exceed the max_join_size system + * variable, and to reduce the database connection timeout to 5 seconds: + * + * @code + * $databases['default']['default'] = array( + * 'init_commands' => array( + * 'big_selects' => 'SET SQL_BIG_SELECTS=1', + * ), + * 'pdo' => array( + * PDO::ATTR_TIMEOUT => 5, + * ), + * ); + * @endcode + * + * WARNING: These defaults are designed for database portability. Changing them + * may cause unexpected behavior, including potential data loss. + * + * @see DatabaseConnection_mysql::__construct + * @see DatabaseConnection_pgsql::__construct + * @see DatabaseConnection_sqlite::__construct + * * Database configuration format: * @code * $databases['default']['default'] = array( diff --git a/themes/bartik/bartik.info b/themes/bartik/bartik.info index 7af315a7bdbaadb2e44d5867441cb5adee404ebf..d898a73f6a17fb9cd3e3898910cac310b5facf0c 100644 --- a/themes/bartik/bartik.info +++ b/themes/bartik/bartik.info @@ -34,8 +34,8 @@ regions[footer] = Footer settings[shortcut_module_link] = 0 -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/bartik/css/style-rtl.css b/themes/bartik/css/style-rtl.css index d25006faaac45380dc6609b483d7abfc27b43999..1a6b17b11c3ab776f07d10428d8009efaf343b4f 100644 --- a/themes/bartik/css/style-rtl.css +++ b/themes/bartik/css/style-rtl.css @@ -96,6 +96,7 @@ ul.tips { float: right; } .link-wrapper { + text-align: left; margin-right: 236px; margin-left: 0; } diff --git a/themes/garland/garland.info b/themes/garland/garland.info index dc48613dd0b62c26bac90034530f323ea2dbcac2..c5adc9abe704c9c82529889507e7ab9b06de0de4 100644 --- a/themes/garland/garland.info +++ b/themes/garland/garland.info @@ -7,8 +7,8 @@ stylesheets[all][] = style.css stylesheets[print][] = print.css settings[garland_width] = fluid -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/seven/maintenance-page.tpl.php b/themes/seven/maintenance-page.tpl.php index aeef310df82b1f4a753dfee536320bd07b515f8c..a158dde393cf4edd45c4a68c50c25f6a0355dbf6 100644 --- a/themes/seven/maintenance-page.tpl.php +++ b/themes/seven/maintenance-page.tpl.php @@ -1,5 +1,4 @@ -<?php -?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>"> <head> diff --git a/themes/seven/page.tpl.php b/themes/seven/page.tpl.php index b9d0ad554cd67bc3ef15c4f9c71e2af86a5f490e..4d40cf89c075032745b886d38e218534ed103a86 100644 --- a/themes/seven/page.tpl.php +++ b/themes/seven/page.tpl.php @@ -1,5 +1,4 @@ -<?php -?> + <div id="branding" class="clearfix"> <?php print $breadcrumb; ?> <?php print render($title_prefix); ?> diff --git a/themes/seven/seven.info b/themes/seven/seven.info index d4dcda2bfcd82aacddacfb36761822eb4f48b8d5..708da9e32e2f5d6ba25149d8b77ab8bb9fae0c8f 100644 --- a/themes/seven/seven.info +++ b/themes/seven/seven.info @@ -13,8 +13,8 @@ regions[page_bottom] = Page bottom regions[sidebar_first] = First sidebar regions_hidden[] = sidebar_first -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/seven/style.css b/themes/seven/style.css index d2da561e992bf9c9ac1c1c21f7b391e5d671b98d..8bb80265a83ff3700b67abcbfc7ba5da3a0ad262 100644 --- a/themes/seven/style.css +++ b/themes/seven/style.css @@ -835,6 +835,7 @@ body.in-maintenance .form-submit { } body.in-maintenance #logo { margin-bottom: 1.5em; + max-width: 180px; } ol.task-list { margin-left: 0; /* LTR */ diff --git a/themes/stark/stark.info b/themes/stark/stark.info index 8eb875e7a6d465738a3c1a2bac1d4aa37effb864..f6f8ae94ee01827084b67a6163fd7bde00c6ca98 100644 --- a/themes/stark/stark.info +++ b/themes/stark/stark.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x stylesheets[all][] = layout.css -; Information added by drupal.org packaging script on 2011-12-05 -version = "7.10" +; Information added by drupal.org packaging script on 2012-02-01 +version = "7.12" project = "drupal" -datestamp = "1323125439" +datestamp = "1328134560" diff --git a/themes/tests/README.txt b/themes/tests/README.txt deleted file mode 100644 index 5ddaa8caf22eedc2dc5e854f695b4a89614710ad..0000000000000000000000000000000000000000 --- a/themes/tests/README.txt +++ /dev/null @@ -1,4 +0,0 @@ - -The themes in this subdirectory are all used by the Drupal core testing -framework. They are not functioning themes that could be used on a real site -and are hidden in the administrative user interface.