diff --git a/.htaccess.sample b/.htaccess.sample index 92a50e2df4b8ea244447b3cb47dd44e3af3a711a..0ac673e08ebd3ae3329d7117d7c3f4a36f3d3461 100644 --- a/.htaccess.sample +++ b/.htaccess.sample @@ -65,7 +65,7 @@ DirectoryIndex index.php index.html index.htm # THIS SECTION IS AUTOMATICALY GENERATED. # DO NOT EDIT!!!! - RewriteRule .*/cron cron.php + RewriteRule .*/cron.php cron.php RewriteRule .*/update.php update.php # %UNL_CREATION_TOOL_STUB% @@ -119,6 +119,31 @@ DirectoryIndex index.php index.html index.htm RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !=/favicon.ico RewriteRule ^ index.php [L] + + # Rules to correctly serve gzip compressed CSS and JS files. + # Requires both mod_rewrite and mod_headers to be enabled. + <IfModule mod_headers.c> + # Serve gzip compressed CSS files if they exist and the client accepts gzip. + RewriteCond %{HTTP:Accept-encoding} gzip + RewriteCond %{REQUEST_FILENAME}\.gz -s + RewriteRule ^(.*)\.css $1\.css\.gz [QSA] + + # Serve gzip compressed JS files if they exist and the client accepts gzip. + RewriteCond %{HTTP:Accept-encoding} gzip + RewriteCond %{REQUEST_FILENAME}\.gz -s + RewriteRule ^(.*)\.js $1\.js\.gz [QSA] + + # Serve correct content types, and prevent mod_deflate double gzip. + RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1] + RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1] + + <FilesMatch "(\.js\.gz|\.css\.gz)$"> + # Serve correct encoding type. + Header append Content-Encoding gzip + # Force proxies to cache gzipped & non-gzipped css/js files separately. + Header append Vary Accept-Encoding + </FilesMatch> + </IfModule> </IfModule> -# $Id: .htaccess,v 1.109 2010/05/05 06:15:59 webchick Exp $ +# $Id: .htaccess,v 1.110 2010/10/11 23:49:48 dries Exp $ diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 1c56966077d662ceea11d1ed8175222ad3d30620..3ba64860ce22940ea233cb35ed876f9b1b0235bd 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,6 @@ -// $Id: CHANGELOG.txt,v 1.373 2010/10/07 03:32:59 webchick Exp $ +// $Id: CHANGELOG.txt,v 1.375 2010/10/23 05:30:57 webchick Exp $ -Drupal 7.0 beta 1, 2010-10-06 +Drupal 7.0 beta 2, 2010-10-22 (development version) ---------------------- - Database: * Fully rewritten database layer utilizing PHP 5's PDO abstraction layer. diff --git a/INSTALL.txt b/INSTALL.txt index c8dc7ba1ab279116baf2c37cd138dff5c0d3206a..09931ed3e9dc0d2fa7388c5efd88925b4049f078 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,4 +1,4 @@ -// $Id: INSTALL.txt,v 1.83 2010/09/22 01:49:17 webchick Exp $ +// $Id: INSTALL.txt,v 1.84 2010/10/11 23:49:48 dries Exp $ CONTENTS OF THIS FILE --------------------- @@ -44,6 +44,9 @@ OPTIONAL TASKS Clean URLs support on IIS, see "Using Clean URLs with IIS" (http://drupal.org/node/3854) in the Drupal handbook. +- To serve gzip compressed CSS and JS files on an Apache web server, you will + need the mod_headers module and the ability to use local .htaccess files. + - Various Drupal features require that the web server process (for example, httpd) be able to initiate outbound connections. This is usually possible, but some hosting providers or server configurations forbid such diff --git a/UPGRADE.txt b/UPGRADE.txt index 55cf1b4637fb5922595eec62b2da0d6d9c12ad1a..fb87a70ee98ab8f7a596d1ba8f7f15988c1169d9 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -1,114 +1,223 @@ -// $Id: UPGRADE.txt,v 1.25 2010/10/04 07:20:38 webchick Exp $ +// $Id: UPGRADE.txt,v 1.26 2010/10/22 00:31:59 dries Exp $ -UPGRADING ---------- +INTRODUCTION +------------ +This document describes how to: -Prior to upgrading, you should ensure that: + * Update your Drupal site from one minor 7.x version to another minor 7.x + version; for example, from 7.9 to 7.10. - * Your system meets or exceeds Drupal's minimum requirements as shown at - http://drupal.org/requirements. - * You have a backup of all your relevant data (#1). - * Custom and contributed modules have been checked for compatibility (#11). - * Custom and contributed themes have been checked for compatibility (#11). - * You have read through this entire document. + * Upgrade your Drupal site's major version from 6.x to 7.x. -If you are upgrading from one major version to another (i.e., 6.x to 7.x), -you must first upgrade to the latest version of your current release. +First steps and definitions: -Let's begin! + * If you are upgrading to Drupal version x.y, then x is known as the major + version number, and y is known as the minor version number. The download + file will be named drupal-x.y.tar.gz. -1. Back up your Drupal database and site root directory. Be especially sure - to back up your "sites" directory which contains your configuration file, - added modules and themes, and your site's uploaded files. If other files - have modifications, such as .htaccess or robots.txt, back those up as well. + * The "Don't hack core" principle is respected. If you modified core files + outside of the 'sites' directory, see http://drupal.org/node/144376. - Note: for a single site setup, the configuration file is the "settings.php" - file located at sites/default/settings.php. The default.settings.php file - contains a clean copy for restoration purposes, if required. + * All directories mentioned in this document are always relative to the + directory of your Drupal installation. - For multisite configurations, the configuration file is located in a - structure like the following: + * Make a full backup of all files, directories, and your database(s) before + starting. Instructions may be found at + http://drupal.org/upgrade/backing-up-the-db - sites/default/settings.php - sites/example.com/settings.php - sites/sub.example.com/settings.php - sites/sub.example.com.path/settings.php - More information on multisite configuration is located in INSTALL.txt. +UPGRADE PROBLEMS +---------------- +If you encounter errors during this process, -2. If possible, log on either as a user with the "Administer software updates" - permission or as the user with user ID 1, which is the first account - created (also known as the site maintenance account). Only these accounts - will be able to automatically access update.php in step #10. There are - special instructions in step #10 if you are unable to log on as one of - these users. Do not close your browser until the final step is complete. + * Note any error messages you see. -3. Place the site in "Offline" mode, to let the database updates run without - interruption and avoid displaying errors to end users of the site. This - option is at http://www.example.com/?q=admin/config/development/maintenance - (replace www.example.com with your installation's domain name and path). + * Restore your site to its previous state, using the file and database backups + you created before you started the upgrade process. Do not attempt to do + further upgrades on a site that had update problems. -4. If using a custom or contributed theme, switch to a core theme such as - Bartik or Garland. + * Consult one of the support options listed on http://drupal.org/support. -5. Disable all custom and contributed modules. This includes any modules that - are not listed under 'Core - required' or 'Core - optional' on - http://www.example.com/?q=admin/build/modules (replace www.example.com with - your installation's domain name and path). +More in-depth information on upgrading can be found at http://drupal.org/upgrade -6. Remove all old files and directories from the Drupal installation directory. -7. Unpack the new files and directories into the Drupal installation directory. +MINOR VERSION UPDATES +--------------------- +To update from one minor 7.x version of Drupal to another 7.x version, after +following the instructions in the INTRODUCTION section at the top of this file: -8. Copy your backed up "files" and "sites" directories to the Drupal - installation directory. If other system files such as .htaccess or - robots.txt were customized, re-create the modifications in the new - versions of the files using the backups taken in step #1. +1. Log in as a user with the permission "Administer software updates". + IMPORTANT! Do not close your browser until the final step is complete. -9. Verify the new configuration file to make sure it has correct information. +2. Go to Administer » Configuration » Development » Maintenance mode. + Enable the "Put site into maintenance mode" checkbox and save the + configuration. -10. Run update.php by visiting http://www.example.com/update.php (replace - www.example.com with your Drupal installation's domain name and path). This - step will update the core database tables to the new Drupal installation. +3. Remove all old core files and directories, EXCEPT for the 'sites' directory + and any custom files you added elsewhere. - Note: if you are unable to access update.php do the following: + 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. - - Open your settings.php file with a text editor. +4. Download the latest Drupal 7.x release from http://drupal.org to a + directory outside of your web root. Extract the archive and copy the files + into your Drupal directory. - - There is a line that says $update_free_access = FALSE; - Change it to $update_free_access = TRUE; + On a typical Unix/Linux command line, use the following commands to download + and extract: - - Once update.php is done, you must change the settings.php file - back to its original form with $update_free_access = FALSE; + wget http://drupal.org/files/projects/drupal-x.y.tar.gz + tar -zxvf drupal-x.y.tar.gz -11. Ensure that the versions of all custom and contributed modules match the - new Drupal version to which you have updated. For a major update, such as - from 6.x to 7.x, modules from previous versions will not be compatible - and updated versions will be required. + This creates a new directory drupal-x.y/ containing all Drupal files and + directories. Copy the files into your Drupal installation directory: - - For contributed modules, check http://drupal.org/project/modules - for the version of a module matching your version of Drupal. + cp -R drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation - - For custom modules, review http://drupal.org/update/modules to - ensure that a custom module is compatible with the current version. + If you do not have command line access to your server, download the archive + from http://drupal.org using your web browser, extract it, and then use an + FTP client to upload the files to your web root. -12. Re-enable custom and contributed modules and re-run update.php - to update custom and contributed database tables. +5. Re-apply any modifications to files such as .htaccess or robots.txt. -13. Return the site to its original theme (if you switched to a core theme in - step #4). If your site uses a custom or contributed theme, make sure it is - compatible with your version of Drupal. +6. 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. - - For contributed themes, check http://drupal.org/project/themes - for the version of a theme matching your version of Drupal. + If you are unable to access update.php do the following: - - For custom themes, review http://drupal.org/update/theme to ensure - that a custom theme is compatible with the current version. + - Open settings.php with a text editor. -14. Finally, return your site to "Online" mode so your visitors may resume - browsing. As in step #3, this option is available in your administration - screens at http://www.example.com/?q=admin/config/development/maintenance - (replace www.example.com with your installation's domain name and path). + - There is a line that says: + $update_free_access = FALSE; + + - Change it into: + $update_free_access = TRUE; + + - Once the upgrade is done, $update_free_access must be reverted to FALSE. + +7. Go to Administration » Reports » Status report. Verify that everything is + is working as expected. + +8. Ensure that $update_free_access is FALSE in settings.php. + +9. Go to Administration » Configuration » Development » Maintenance mode. + Disable the "Put site into maintenance mode" checkbox and save the + configuration. + + +MAJOR VERSION UPGRADE +--------------------- +To upgrade from a previous major version of Drupal to Drupal 7.x, after +following the instructions in the INTRODUCTION section at the top of this file: + +1. Update to the latest available version of Drupal 6.x. + + If your current version is Drupal 5.x, you have to upgrade to 6.x first. + Download Drupal 6.x and follow the instructions in UPGRADE.txt instead. + This document only applies for upgrades from 6.x to 7.x. + +2. Check the availability of your modules and themes for Drupal 7.x. See + http://drupal.org/node/948216 + +3. Log in as user ID 1 (the site maintenance user). + +4. 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 + default theme. + +6. 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. + + In case you already know that you will not re-enable some modules for Drupal + 7.x and you no longer need their data, then you can uninstall them under the + Uninstall tab afterwards. See http://drupal.org/node/895314 for a list of + modules whose functionality has been moved into core for Drupal 7.x. + +7. 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 + 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 + 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 + directory outside of your web root. Extract the archive and copy the files + into your Drupal directory. + + On a typical Unix/Linux command line, use the following commands to download + and extract: + + wget http://drupal.org/files/projects/drupal-x.y.tar.gz + tar -zxvf drupal-x.y.tar.gz + + This creates a new directory drupal-x.y/ containing all Drupal files and + directories. Copy the files into your Drupal installation directory: + + cp -R drupal-x.y/* drupal-x.y/.htaccess /path/to/your/installation + + If you do not have command line access to your server, download the archive + 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. 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 + www.example.com with your domain name). This will update the core database + tables. + + If you are unable to access update.php do the following: + + - Open settings.php with a text editor. + + - There is a line that says: + $update_free_access = FALSE; + + - Change it into: + $update_free_access = TRUE; + + - Once the upgrade is done, $update_free_access must be reverted to FALSE. + +14. Backup your database after the core upgrade has run. + +15. Replace your non-core modules, following this procedure: + + - Check your notes for any special upgrade instructions. + - Entirely delete the old module directory in sites/all/modules/. + - Download, extract, and move the new module directory to sites/all/modules/. + +16. Go to Administration » Modules. Re-enable your non-core modules. + +17. Re-run update.php. + +18. If applicable, return the site to its original theme following the same + procedure as in 15., but putting the theme in sites/all/themes/. + +19. Go to Administration » Reports » Status report. Verify that everything is + is working as expected. + +20. Ensure that $update_free_access is FALSE in settings.php. + +21. Go to Administration » Configuration » Development » Maintenance mode. + Disable the "Put site into maintenance mode" checkbox and save the + configuration. + +To get started with Drupal 7 administration, visit +http://drupal.org/getting-started/7/admin -For more information on upgrading, visit -the Drupal handbook at http://drupal.org/upgrade diff --git a/includes/ajax.inc b/includes/ajax.inc index 6f7b937908609092f54e0bdef4ebeef78d53c1e1..8ad3bc36e9767bd4ae6b2f5abc47bdc6516967dd 100644 --- a/includes/ajax.inc +++ b/includes/ajax.inc @@ -1,5 +1,5 @@ <?php -// $Id: ajax.inc,v 1.36 2010/10/06 23:03:39 webchick Exp $ +// $Id: ajax.inc,v 1.37 2010/10/21 19:31:39 dries Exp $ /** * @file @@ -515,7 +515,26 @@ function ajax_footer() { } /** - * Add AJAX information about a form element to the page to communicate with JavaScript. + * Form element process callback to handle #ajax. + * + * @param $element + * An associative array containing the properties of the element. + * + * @return + * The processed element. + * + * @see ajax_pre_render_element() + */ +function ajax_process_form($element, &$form_state) { + $element = ajax_pre_render_element($element); + if (!empty($element['#ajax_processed'])) { + $form_state['cache'] = TRUE; + } + return $element; +} + +/** + * Add AJAX information about an element to the page 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 @@ -526,15 +545,22 @@ function ajax_footer() { * Properties used: * - #ajax['event'] * - #ajax['path'] + * - #ajax['options'] * - #ajax['wrapper'] * - #ajax['parameters'] * - #ajax['effect'] * * @return - * None. Additional code is added to the header of the page using - * drupal_add_js(). + * The processed element with the necessary JavaScript attached to it. */ -function ajax_process_form($element, &$form_state) { +function ajax_pre_render_element($element) { + // Skip already processed elements. + if (isset($element['#ajax_processed'])) { + return $element; + } + // Initialize #ajax_processed, so we do not process this element again. + $element['#ajax_processed'] = FALSE; + // Nothing to do if there is neither a callback nor a path. if (!(isset($element['#ajax']['callback']) || isset($element['#ajax']['path']))) { return $element; @@ -567,6 +593,10 @@ function ajax_process_form($element, &$form_state) { $element['#ajax']['event'] = 'change'; break; + case 'link': + $element['#ajax']['event'] = 'click'; + break; + default: return $element; } @@ -581,6 +611,8 @@ function ajax_process_form($element, &$form_state) { // Assign default settings. $settings += array( + 'path' => 'system/ajax', + 'options' => array(), 'selector' => '#' . $element['#id'], 'effect' => 'none', 'speed' => 'none', @@ -593,9 +625,9 @@ function ajax_process_form($element, &$form_state) { $settings['method'] = 'replaceWith'; } - // Change path to url. - $settings['url'] = isset($settings['path']) ? url($settings['path']) : url('system/ajax'); - unset($settings['path']); + // Change path to URL. + $settings['url'] = url($settings['path'], $settings['options']); + unset($settings['path'], $settings['options']); // Add special data to $settings['submit'] so that when this element // triggers an AJAX submission, Drupal's form processing can determine which @@ -614,7 +646,7 @@ function ajax_process_form($element, &$form_state) { } unset($settings['trigger_as']); } - else { + elseif (isset($element['#name'])) { // Most of the time, elements can submit as themselves, in which case the // 'trigger_as' key isn't needed, and the element's name is used. $settings['submit']['_triggering_element_name'] = $element['#name']; @@ -645,7 +677,8 @@ function ajax_process_form($element, &$form_state) { 'data' => array('ajax' => array($element['#id'] => $settings)), ); - $form_state['cache'] = TRUE; + // Indicate that AJAX processing was successful. + $element['#ajax_processed'] = TRUE; } return $element; } diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 39ba0e7a3ba05981a3ebe1e4e4de5ec0b8eea3d2..06bfddbbb649d82518f8b63d742e88d64cdfe798 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1,5 +1,5 @@ <?php -// $Id: bootstrap.inc,v 1.422 2010/10/07 03:32:59 webchick Exp $ +// $Id: bootstrap.inc,v 1.430 2010/10/23 05:30:57 webchick Exp $ /** * @file @@ -9,7 +9,7 @@ /** * The current system version. */ -define('VERSION', '7.0-beta1'); +define('VERSION', '7.0-beta2'); /** * Core API compatibility. @@ -19,12 +19,12 @@ define('DRUPAL_CORE_COMPATIBILITY', '7.x'); /** * Minimum supported version of PHP. */ -define('DRUPAL_MINIMUM_PHP', '5.2.0'); +define('DRUPAL_MINIMUM_PHP', '5.2.4'); /** * Minimum recommended value of PHP memory_limit. */ -define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '40M'); +define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M'); /** * Minimum supported version of MySQL, if it is used. @@ -230,12 +230,6 @@ define('LANGUAGE_RTL', 1); */ define('REQUEST_TIME', $_SERVER['REQUEST_TIME']); -/** - * @name Title text filtering flags - * @{ - * Flags for use in drupal_set_title(). - */ - /** * Flag for drupal_set_title(); text is not sanitized, so run check_plain(). */ @@ -246,10 +240,6 @@ define('CHECK_PLAIN', 0); */ define('PASS_THROUGH', -1); -/** - * @} End of "Title text filtering flags". - */ - /** * Signals that the registry lookup cache should be reset. */ @@ -1481,25 +1471,7 @@ function t($string, array $args = array(), array $options = array()) { * @ingroup sanitization */ function check_plain($text) { - // We do not want to use drupal_static() since PHP version will never change - // during a request. - static $php525; - - if (!isset($php525)) { - $php525 = version_compare(PHP_VERSION, '5.2.5', '>='); - } - // We duplicate the preg_match() to validate strings as UTF-8 from - // drupal_validate_utf8() here. This avoids the overhead of an additional - // function call, since check_plain() may be called hundreds of times during - // a request. For PHP 5.2.5+, this check for valid UTF-8 should be handled - // internally by PHP in htmlspecialchars(). - // See http://www.php.net/releases/5_2_5.php. - // @todo remove this when support for either IE6 or PHP < 5.2.5 is dropped. - - if ($php525) { - return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); - } - return (preg_match('/^./us', $text) == 1) ? htmlspecialchars($text, ENT_QUOTES, 'UTF-8') : ''; + return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); } /** @@ -1911,13 +1883,12 @@ function drupal_hash_base64($data) { * * @return Object - the user object. */ -function drupal_anonymous_user($session = '') { +function drupal_anonymous_user() { $user = new stdClass(); $user->uid = 0; $user->hostname = ip_address(); $user->roles = array(); $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; - $user->session = $session; $user->cache = 0; return $user; } @@ -2386,6 +2357,12 @@ function language_list($field = 'language') { if (!isset($languages)) { if (drupal_multilingual() || module_exists('locale')) { $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language'); + // Users cannot uninstall the native English language. However, we allow + // it to be hidden from the installed languages. Therefore, at least one + // other language must be enabled then. + if (!$languages['language']['en']->enabled && !variable_get('language_native_enabled', TRUE)) { + unset($languages['language']['en']); + } } else { // No locale module, so use the default language only. diff --git a/includes/cache.inc b/includes/cache.inc index a625266bff5fe7eb53cb680f2061a646c23884ec..cab2e9f14b89cd5a33e6e5a2828542e4b698b301 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -1,5 +1,5 @@ <?php -// $Id: cache.inc,v 1.48 2010/05/18 18:26:30 dries Exp $ +// $Id: cache.inc,v 1.49 2010/10/07 17:44:53 dries Exp $ /** * Get the cache object for a cache bin. @@ -155,7 +155,7 @@ function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) { * @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 table $table is emptied. + * TRUE and $cid is '*' then the entire bin $bin is emptied. */ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) { if (!isset($cid) && !isset($bin)) { diff --git a/includes/common.inc b/includes/common.inc index 51a352fdf42713bafc0e613fb28e274f5b30af92..cd5e6706e72b443f0642eed1b42eb51312beee51 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1,5 +1,5 @@ <?php -// $Id: common.inc,v 1.1236 2010/10/07 00:28:19 webchick Exp $ +// $Id: common.inc,v 1.1244 2010/10/21 19:31:39 dries Exp $ /** * @file @@ -378,7 +378,7 @@ function drupal_get_feeds($delimiter = "\n") { } /** - * @name HTTP handling + * @defgroup http_handling HTTP handling * @{ * Functions to properly handle HTTP responses. */ @@ -1241,7 +1241,7 @@ function drupal_strip_dangerous_protocols($uri) { static $allowed_protocols; if (!isset($allowed_protocols)) { - $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('ftp', 'http', 'https', 'irc', 'mailto', 'news', 'nntp', 'rtsp', 'sftp', 'ssh', 'telnet', 'webcal'))); + $allowed_protocols = array_flip(variable_get('filter_allowed_protocols', array('ftp', 'http', 'https', 'irc', 'mailto', 'news', 'nntp', 'rtsp', 'sftp', 'ssh', 'tel', 'telnet', 'webcal'))); } // Iteratively remove any invalid protocol found. @@ -3369,6 +3369,15 @@ function drupal_build_css_cache($css) { if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) { return FALSE; } + // If CSS gzip compression is enabled, clean URLs are enabled (which means + // that rewrite rules are working) and the zlib extension is available then + // create a gzipped version of this file. This file is served conditionally + // to browsers that accept gzip using .htaccess rules. + if (variable_get('css_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) { + if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) { + return FALSE; + } + } // Save the updated map. $map[$key] = $uri; variable_set('drupal_css_cache_files', $map); @@ -3466,13 +3475,34 @@ function drupal_load_stylesheet_content($contents, $optimize = FALSE) { $double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; // Regexp to match single quoted strings. $single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'"; + // Strip all comment blocks, but keep double/single quoted strings. $contents = preg_replace( - "<($double_quot|$single_quot)|$comment>Ss", // Strip all comment blocks - "$1", // but keep double/single - $contents); // quoted strings. - $contents = preg_replace( - '<\s*([@{}:;,]|\)\s|\s\()\s*>S', // Remove whitespace around separators, - '\1', $contents); // but keep space around parentheses. + "<($double_quot|$single_quot)|$comment>Ss", + "$1", + $contents + ); + // Remove certain whitespace. + // There are different conditions for removing leading and trailing + // whitespace. To be able to use a single backreference in the replacement + // string, the outer pattern uses the ?| modifier, which makes all contained + // subpatterns appear in \1. + // @see http://php.net/manual/en/regexp.reference.subpatterns.php + $contents = preg_replace('< + (?| + # Strip leading and trailing whitespace. + \s*([@{};,])\s* + # Strip only leading whitespace from: + # - Closing parenthesis: Retain "@media (bar) and foo". + | \s+([\)]) + # Strip only trailing whitespace from: + # - Opening parenthesis: Retain "@media (bar) and foo". + # - Colon: Retain :pseudo-selectors. + | ([\(:])\s+ + ) + >xS', + '\1', + $contents + ); // End the file with a new line. $contents .= "\n"; } @@ -3767,7 +3797,7 @@ function drupal_region_class($region) { * - scope: The location in which you want to place the script. Possible * values are 'header' or 'footer'. If your theme implements different * regions, you can also use these. Defaults to 'header'. - * - 'group': A number identifying the group in which to add the JavaScript. + * - group: A number identifying the group in which to add the JavaScript. * Available constants are: * - JS_LIBRARY: Any libraries, settings, or jQuery plugins. * - JS_DEFAULT: Any module-layer JavaScript. @@ -3775,7 +3805,7 @@ function drupal_region_class($region) { * The group number serves as a weight: JavaScript within a lower weight * group is presented on the page before JavaScript within a higher weight * group. - * - 'every_page': For optimal front-end performance when aggregation is + * - every_page: For optimal front-end performance when aggregation is * enabled, this should be set to TRUE if the JavaScript is present on every * page of the website for users for whom it is present at all. This * defaults to FALSE. It is set to TRUE for JavaScript files that are added @@ -4637,9 +4667,18 @@ function drupal_build_js_cache($files) { $uri = $jspath . '/' . $filename; // Create the JS file. file_prepare_directory($jspath, FILE_CREATE_DIRECTORY); - if (!file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) { + if (!file_exists($uri) && !file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) { return FALSE; } + // If JS gzip compression is enabled, clean URLs are enabled (which means + // that rewrite rules are working) and the zlib extension is available then + // create a gzipped version of this file. This file is served conditionally + // to browsers that accept gzip using .htaccess rules. + if (variable_get('js_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) { + if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($contents, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) { + return FALSE; + } + } $map[$key] = $uri; variable_set('drupal_js_cache_files', $map); } @@ -4965,7 +5004,8 @@ function drupal_cron_cleanup() { * the file name ($key = 'filename'), the file name without the extension ($key * = 'name'), or the full file stream URI ($key = 'uri'). If you use a key of * 'filename' or 'name', files found later in the search will take precedence - * over files found earlier; if you choose a key of 'uri', you will get all + * over files found earlier (unless they belong to a module or theme not + * compatible with Drupal core); if you choose a key of 'uri', you will get all * files found. * * @param string $mask @@ -5019,7 +5059,31 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) require_once DRUPAL_ROOT . '/includes/file.inc'; } foreach ($searchdir as $dir) { - $files = array_merge($files, file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth))); + $files_to_add = file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth)); + + // Duplicate files found in later search directories take precedence over + // earlier ones, so we want them to overwrite keys in our resulting + // $files array. + // The exception to this is if the later file is from a module or theme not + // compatible with Drupal core. This may occur during upgrades of Drupal + // core when new modules exist in core while older contrib modules with the + // same name exist in a directory such as sites/all/modules/. + foreach (array_intersect_key($files_to_add, $files) as $key => $file) { + // If it has no info file, then we just behave liberally and accept the + // new resource on the list for merging. + if (file_exists($info_file = dirname($file->uri) . '/' . $file->name . '.info')) { + // Get the .info file for the module or theme this file belongs to. + $info = drupal_parse_info_file($info_file); + + // If the module or theme is incompatible with Drupal core, remove it + // from the array for the current search directory, so it is not + // overwritten when merged with the $files array. + if (isset($info['core']) && $info['core'] != DRUPAL_CORE_COMPATIBILITY) { + unset($files_to_add[$key]); + } + } + } + $files = array_merge($files, $files_to_add); } return $files; @@ -5145,10 +5209,44 @@ function drupal_pre_render_conditional_comments($elements) { * @return * The passed in elements containing a rendered link in '#markup'. */ -function drupal_pre_render_link($elements) { - $options = isset($elements['#options']) ? $elements['#options'] : array(); - $elements['#markup'] = l($elements['#title'], $elements['#href'], $options); - return $elements; +function drupal_pre_render_link($element) { + // By default, link options to pass to l() are normally set in #options. + $element += array('#options' => array()); + // However, within the scope of renderable elements, #attributes is a valid + // way to specify attributes, too. Take them into account, but do not override + // attributes from #options. + if (isset($element['#attributes'])) { + $element['#options'] += array('attributes' => array()); + $element['#options']['attributes'] += $element['#attributes']; + } + + // This #pre_render callback can be invoked from inside or outside of a Form + // API context, and depending on that, a HTML ID may be already set in + // different locations. #options should have precedence over Form API's #id. + // #attributes have been taken over into #options above already. + if (isset($element['#options']['attributes']['id'])) { + $element['#id'] = $element['#options']['attributes']['id']; + } + elseif (isset($element['#id'])) { + $element['#options']['attributes']['id'] = $element['#id']; + } + + // Conditionally invoke ajax_pre_render_element(), if #ajax is set. + if (isset($element['#ajax']) && !isset($element['#ajax_processed'])) { + // If no HTML ID was found above, automatically create one. + if (!isset($element['#id'])) { + $element['#id'] = $element['#options']['attributes']['id'] = drupal_html_id('ajax-link'); + } + // If #ajax['path] was not specified, use the href as AJAX request URL. + if (!isset($element['#ajax']['path'])) { + $element['#ajax']['path'] = $element['#href']; + $element['#ajax']['options'] = $element['#options']; + } + $element = ajax_pre_render_element($element); + } + + $element['#markup'] = l($element['#title'], $element['#href'], $element['#options']); + return $element; } /** @@ -6780,7 +6878,11 @@ function _drupal_flush_css_js() { */ function debug($data, $label = NULL, $print_r = FALSE) { // Print $data contents to string. - $string = $print_r ? print_r($data, TRUE) : var_export($data, TRUE); + $string = check_plain($print_r ? print_r($data, TRUE) : var_export($data, TRUE)); + + // Display values with pre-formatting to increase readability. + $string = '<pre>' . $string . '</pre>'; + trigger_error(trim($label ? "$label: $string" : $string)); } diff --git a/includes/database/database.inc b/includes/database/database.inc index 52e6e51fadd0809ce3d4aa98bfe2113e5adae9cc..104d76f77c8075b09bf213084fc84a2132ee5a0c 100644 --- a/includes/database/database.inc +++ b/includes/database/database.inc @@ -1,5 +1,5 @@ <?php -// $Id: database.inc,v 1.140 2010/10/03 01:29:40 dries Exp $ +// $Id: database.inc,v 1.141 2010/10/15 18:03:43 webchick Exp $ /** * @file @@ -1293,6 +1293,9 @@ abstract class Database { /** * Gets the connection object for the specified database key and target. * + * Note: do not use the setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) on the + * returned object because of http://bugs.php.net/bug.php?id=43139. + * * @param $target * The database target name. * @param $key diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc index f042a217383f00654e5532158f1e718c1677b5ea..05fe2d5209aa60798cb64abd8bdb1e11de86d386 100644 --- a/includes/database/mysql/schema.inc +++ b/includes/database/mysql/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.42 2010/09/25 01:41:26 dries Exp $ +// $Id: schema.inc,v 1.43 2010/10/22 15:18:56 webchick Exp $ /** * @file @@ -132,7 +132,7 @@ class DatabaseSchema_mysql extends DatabaseSchema { protected function createFieldSql($name, $spec) { $sql = "`" . $name . "` " . $spec['mysql_type']; - if (in_array($spec['type'], array('varchar', 'char', 'text')) && isset($spec['length'])) { + if (in_array($spec['mysql_type'], array('VARCHAR', 'CHAR', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT')) && isset($spec['length'])) { $sql .= '(' . $spec['length'] . ')'; } elseif (isset($spec['precision']) && isset($spec['scale'])) { @@ -143,8 +143,13 @@ class DatabaseSchema_mysql extends DatabaseSchema { $sql .= ' unsigned'; } - if (!empty($spec['not null'])) { - $sql .= ' NOT NULL'; + if (isset($spec['not null'])) { + if ($spec['not null']) { + $sql .= ' NOT NULL'; + } + else { + $sql .= ' NULL'; + } } if (!empty($spec['auto_increment'])) { @@ -187,12 +192,16 @@ class DatabaseSchema_mysql extends DatabaseSchema { } // Set the correct database-engine specific datatype. - if (!isset($field['mysql_type'])) { + // In case one is already provided, force it to uppercase. + if (isset($field['mysql_type'])) { + $field['mysql_type'] = drupal_strtoupper($field['mysql_type']); + } + else { $map = $this->getFieldTypeMap(); $field['mysql_type'] = $map[$field['type'] . ':' . $field['size']]; } - if ($field['type'] == 'serial') { + if (isset($field['type']) && $field['type'] == 'serial') { $field['auto_increment'] = TRUE; } diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc index cb08207d98d28243be42699c4974b247d006cf39..0cfd8a4b1b739b627c219f74a581c77751e70239 100644 --- a/includes/database/pgsql/schema.inc +++ b/includes/database/pgsql/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.38 2010/09/25 01:41:26 dries Exp $ +// $Id: schema.inc,v 1.39 2010/10/22 15:18:56 webchick Exp $ /** * @file @@ -145,11 +145,11 @@ class DatabaseSchema_pgsql extends DatabaseSchema { protected function createFieldSql($name, $spec) { $sql = $name . ' ' . $spec['pgsql_type']; - if ($spec['type'] == 'serial') { + if (isset($spec['type']) && $spec['type'] == 'serial') { unset($spec['not null']); } - if (in_array($spec['type'], array('varchar', 'char', 'text')) && isset($spec['length'])) { + if (in_array($spec['pgsql_type'], array('varchar', 'character', 'text')) && isset($spec['length'])) { $sql .= '(' . $spec['length'] . ')'; } elseif (isset($spec['precision']) && isset($spec['scale'])) { @@ -160,8 +160,13 @@ class DatabaseSchema_pgsql extends DatabaseSchema { $sql .= " CHECK ($name >= 0)"; } - if (isset($spec['not null']) && $spec['not null']) { - $sql .= ' NOT NULL'; + if (isset($spec['not null'])) { + if ($spec['not null']) { + $sql .= ' NOT NULL'; + } + else { + $sql .= ' NULL'; + } } if (isset($spec['default'])) { $default = is_string($spec['default']) ? "'" . $spec['default'] . "'" : $spec['default']; @@ -181,11 +186,17 @@ class DatabaseSchema_pgsql extends DatabaseSchema { if (!isset($field['size'])) { $field['size'] = 'normal'; } + // Set the correct database-engine specific datatype. - if (!isset($field['pgsql_type'])) { + // In case one is already provided, force it to lowercase. + if (isset($field['pgsql_type'])) { + $field['pgsql_type'] = drupal_strtolower($field['pgsql_type']); + } + else { $map = $this->getFieldTypeMap(); $field['pgsql_type'] = $map[$field['type'] . ':' . $field['size']]; } + if (!empty($field['unsigned'])) { // Unsigned datatypes are not supported in PostgreSQL 8.3. In MySQL, // they are used to ensure a positive number is inserted and it also @@ -205,7 +216,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { break; } } - if ($field['type'] == 'serial') { + if (isset($field['type']) && $field['type'] == 'serial') { unset($field['not null']); } return $field; @@ -462,19 +473,25 @@ class DatabaseSchema_pgsql extends DatabaseSchema { $spec['size'] = 'normal'; } + // Map type definition to the PostgreSQL type. + if (!isset($spec['pgsql_type'])) { + $map = $this->getFieldTypeMap(); + $spec['pgsql_type'] = $map[$spec['type'] . ':' . $spec['size']]; + } + // We need to typecast the new column to best be able to transfer the data // Schema_pgsql::getFieldTypeMap() will return possibilities that are not // 'cast-able' such as 'serial' - so they need to be casted int instead. - $map = $this->getFieldTypeMap(); - $typecast = $map[$spec['type'] . ':' . $spec['size']]; - if (in_array($typecast, array('serial', 'bigserial', 'numeric'))) { + if (in_array($spec['pgsql_type'], array('serial', 'bigserial', 'numeric'))) { $typecast = 'int'; } + else { + $typecast = $spec['pgsql_type']; + } + $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $typecast . ' USING "' . $field . '"::' . $typecast); - // Map type definition to the PostgreSQL type. - $pgsql_type = $map[$spec['type'] . ':' . $spec['size']]; - if (in_array($pgsql_type, array('serial', 'bigserial'))) { + if (in_array($spec['pgsql_type'], array('serial', 'bigserial'))) { // Type "serial" is known to PostgreSQL, but *only* during table creation, // not when altering. Because of that, the sequence needs to be created // and initialized by hand. diff --git a/includes/database/query.inc b/includes/database/query.inc index 5a00cf6009337e8975c8ea00644ad5536bf01e2d..e6d2830e49ec319c09a03d513867c6f3204ae745 100644 --- a/includes/database/query.inc +++ b/includes/database/query.inc @@ -1,5 +1,5 @@ <?php -// $Id: query.inc,v 1.56 2010/09/24 02:05:55 dries Exp $ +// $Id: query.inc,v 1.57 2010/10/18 00:50:36 dries Exp $ /** * @ingroup database @@ -908,8 +908,8 @@ class UpdateQuery extends Query implements QueryConditionInterface { * instead. MergeQuery::fields() can also be called which calls both of these * methods as the common case is to use the same column-value pairs for both * INSERT and UPDATE. However, this is not mandatory. Another convinient - * wrapper is MergeQuery::key() which adds the same column-value pairs to all - * three parts: the condition, the INSERT query part and the UPDATE query part. + * wrapper is MergeQuery::key() which adds the same column-value pairs to the + * condition and the INSERT query part. * * Several methods (key(), fields(), insertFields()) can be called to set a * key-value pair for the INSERT query part. Subsequent calls for the same @@ -1137,7 +1137,7 @@ class MergeQuery extends Query implements QueryConditionInterface { } /** - * Set the key field(s) to be used everywhere. + * Set the key field(s) to be used as conditions for this query. * * This method should only be called once. It may be called either * with a single associative array or two indexed arrays. If called @@ -1146,9 +1146,8 @@ class MergeQuery extends Query implements QueryConditionInterface { * If called with two arrays, the first array is taken as the fields * and the second array is taken as the corresponding values. * - * The fields are copied to all three parts of the query: the condition, - * the UPDATE part and the INSERT part. If no other method is called, the - * UPDATE will become a no-op. + * The fields are copied to the condition of the query and the INSERT part. + * If no other method is called, the UPDATE will become a no-op. * * @param $fields * An array of fields to set. @@ -1164,7 +1163,6 @@ class MergeQuery extends Query implements QueryConditionInterface { } foreach ($fields as $key => $value) { $this->insertFields[$key] = $value; - $this->updateFields[$key] = $value; $this->condition($key, $value); } return $this; @@ -1226,7 +1224,6 @@ class MergeQuery extends Query implements QueryConditionInterface { return MergeQuery::STATUS_INSERT; } catch (Exception $e) { - echo $e->getMessage(); // The insert query failed, maybe it's because a racing insert query // beat us in inserting the same row. Retry the select query, if it // returns a row, ignore the error and continue with the update diff --git a/includes/database/sqlite/schema.inc b/includes/database/sqlite/schema.inc index 13881e0edd9dde8732c9f72b18a34bbd925011ad..e8a02db95f37229879e880554d2059cd35502795 100644 --- a/includes/database/sqlite/schema.inc +++ b/includes/database/sqlite/schema.inc @@ -1,5 +1,5 @@ <?php -// $Id: schema.inc,v 1.19 2010/07/28 10:52:12 dries Exp $ +// $Id: schema.inc,v 1.20 2010/10/22 15:18:56 webchick Exp $ /** * @file @@ -74,7 +74,7 @@ class DatabaseSchema_sqlite extends DatabaseSchema { // Add the SQL statement for each field. foreach ($schema['fields'] as $name => $field) { - if ($field['type'] == 'serial') { + if (isset($field['type']) && $field['type'] == 'serial') { if (isset($schema['primary key']) && ($key = array_search($name, $schema['primary key'])) !== FALSE) { unset($schema['primary key'][$key]); } @@ -116,13 +116,18 @@ class DatabaseSchema_sqlite extends DatabaseSchema { if (!isset($field['size'])) { $field['size'] = 'normal'; } + // Set the correct database-engine specific datatype. - if (!isset($field['sqlite_type'])) { + // In case one is already provided, force it to uppercase. + if (isset($field['sqlite_type'])) { + $field['sqlite_type'] = drupal_strtoupper($field['sqlite_type']); + } + else { $map = $this->getFieldTypeMap(); $field['sqlite_type'] = $map[$field['type'] . ':' . $field['size']]; } - if ($field['type'] == 'serial') { + if (isset($field['type']) && $field['type'] == 'serial') { $field['auto_increment'] = TRUE; } @@ -150,12 +155,17 @@ class DatabaseSchema_sqlite extends DatabaseSchema { else { $sql = $name . ' ' . $spec['sqlite_type']; - if (in_array($spec['type'], array('varchar', 'char', 'text')) && isset($spec['length'])) { + if (in_array($spec['sqlite_type'], array('VARCHAR', 'TEXT')) && isset($spec['length'])) { $sql .= '(' . $spec['length'] . ')'; } - if (!empty($spec['not null'])) { - $sql .= ' NOT NULL'; + if (isset($spec['not null'])) { + if ($spec['not null']) { + $sql .= ' NOT NULL'; + } + else { + $sql .= ' NULL'; + } } if (!empty($spec['unsigned'])) { diff --git a/includes/entity.inc b/includes/entity.inc index 0d7510dd452e1eb7dd6d42ce09691e779f01cbf4..95c75de95407d6b9db1067afeb2ab2d5a7cbc9f4 100644 --- a/includes/entity.inc +++ b/includes/entity.inc @@ -1,5 +1,5 @@ <?php -// $Id: entity.inc,v 1.15 2010/10/06 13:57:47 dries Exp $ +// $Id: entity.inc,v 1.16 2010/10/09 02:36:46 webchick Exp $ /** * Interface for entity controller classes. @@ -688,7 +688,7 @@ class EntityFieldQuery { * @return EntityFieldQuery * The called object. */ - public function entityOrderBy($name, $direction) { + public function entityOrderBy($name, $direction = 'ASC') { $this->order[] = array( 'type' => 'entity', 'specifier' => $name, @@ -714,7 +714,7 @@ class EntityFieldQuery { * @return EntityFieldQuery * The called object. */ - public function fieldOrderBy($field, $column, $direction) { + public function fieldOrderBy($field, $column, $direction = 'ASC') { if (is_scalar($field)) { $field = field_info_field($field); } @@ -751,7 +751,7 @@ class EntityFieldQuery { * @return EntityFieldQuery * The called object. */ - public function propertyOrderBy($column, $direction) { + public function propertyOrderBy($column, $direction = 'ASC') { $this->order[] = array( 'type' => 'property', 'specifier' => $column, diff --git a/includes/errors.inc b/includes/errors.inc index f26319d3336dea5bdf8e1b6ecd89a096c33fc08e..2022fc4f13e6556a730bed70e2500c1dc5badfa1 100644 --- a/includes/errors.inc +++ b/includes/errors.inc @@ -1,5 +1,5 @@ <?php -// $Id: errors.inc,v 1.8 2010/06/28 20:27:34 dries Exp $ +// $Id: errors.inc,v 1.9 2010/10/16 00:00:16 webchick Exp $ /** * @file @@ -70,10 +70,16 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c list($severity_msg, $severity_level) = $types[$error_level]; $caller = _drupal_get_last_caller(debug_backtrace()); + if (!function_exists('filter_xss_admin')) { + require_once DRUPAL_ROOT . '/includes/common.inc'; + } + // We treat recoverable errors as fatal. _drupal_log_error(array( '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', - '%message' => $message, + // The standard PHP error handler considers that the error messages + // are HTML. We mimick this behavior here. + '!message' => filter_xss_admin($message), '%function' => $caller['function'], '%file' => $caller['file'], '%line' => $caller['line'], @@ -118,7 +124,9 @@ function _drupal_decode_exception($exception) { return array( '%type' => get_class($exception), - '%message' => $message, + // The standard PHP exception handler considers that the exception message + // is plain-text. We mimick this behavior here. + '!message' => check_plain($message), '%function' => $caller['function'], '%file' => $caller['file'], '%line' => $caller['line'], @@ -135,7 +143,7 @@ function _drupal_decode_exception($exception) { * An error message. */ function _drupal_render_exception_safe($exception) { - return check_plain(strtr('%type: %message in %function (line %line of %file).', _drupal_decode_exception($exception))); + return check_plain(strtr('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception))); } /** @@ -165,7 +173,9 @@ function error_displayable($error = NULL) { * Log a PHP error or exception, display an error page in fatal cases. * * @param $error - * An array with the following keys: %type, %message, %function, %file, %line. + * An array with the following keys: %type, !message, %function, %file, %line. + * All the parameters are plain-text, exception message, which needs to be + * a safe HTML string. * @param $fatal * TRUE if the error is fatal. */ @@ -188,7 +198,7 @@ function _drupal_log_error($error, $fatal = FALSE) { // as it uniquely identifies each PHP error. static $number = 0; $assertion = array( - $error['%message'], + $error['!message'], $error['%type'], array( 'function' => $error['%function'], @@ -200,7 +210,7 @@ function _drupal_log_error($error, $fatal = FALSE) { $number++; } - watchdog('php', '%type: %message in %function (line %line of %file).', $error, $error['severity_level']); + watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']); if ($fatal) { drupal_add_http_header('Status', '500 Service unavailable (with message)'); @@ -209,7 +219,7 @@ function _drupal_log_error($error, $fatal = FALSE) { if (drupal_is_cli()) { if ($fatal) { // When called from CLI, simply output a plain text message. - print html_entity_decode(strip_tags(t('%type: %message in %function (line %line of %file).', $error))). "\n"; + print html_entity_decode(strip_tags(t('%type: !message in %function (line %line of %file).', $error))). "\n"; exit; } } @@ -217,7 +227,7 @@ function _drupal_log_error($error, $fatal = FALSE) { if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { if ($fatal) { // When called from JavaScript, simply output the error message. - print t('%type: %message in %function (line %line of %file).', $error); + print t('%type: !message in %function (line %line of %file).', $error); exit; } } @@ -234,7 +244,7 @@ function _drupal_log_error($error, $fatal = FALSE) { $class = 'status'; } - drupal_set_message(t('%type: %message in %function (line %line of %file).', $error), $class); + drupal_set_message(t('%type: !message in %function (line %line of %file).', $error), $class); } if ($fatal) { diff --git a/includes/file.inc b/includes/file.inc index d46c4b15f140d4ba7572b7be799c7a89f4e0c09d..4e9e9e2abe21d099f5643515c78a60bd0ee5e350 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -1,5 +1,5 @@ <?php -// $Id: file.inc,v 1.235 2010/10/05 06:23:18 webchick Exp $ +// $Id: file.inc,v 1.239 2010/10/21 12:09:41 dries Exp $ /** * @file @@ -90,12 +90,37 @@ define('FILE_STATUS_PERMANENT', 1); * * A stream is referenced as "scheme://target". * + * The optional $filter parameter can be used to retrieve only the stream + * wrappers that are appropriate for particular usage. For example, this returns + * only stream wrappers that use local file storage: + * @code + * $local_stream_wrappers = file_get_stream_wrappers(STEAM_WRAPPERS_LOCAL); + * @endcode + * + * The $filter parameter can only filter to types containing a particular flag. + * In some cases, you may want to filter to types that do not contain a + * particular flag. For example, you may want to retrieve all stream wrappers + * that are not writable, or all stream wrappers that are not local. PHP's + * array_diff_key() function can be used to help with this. For example, this + * returns only stream wrappers that do not use local file storage: + * @code + * $remote_stream_wrappers = array_diff_key(file_get_stream_wrappers(STREAM_WRAPPERS_ALL), file_get_stream_wrappers(STEAM_WRAPPERS_LOCAL)); + * @endcode + * * @param $filter - * Optionally filter out all types except these. Defaults to - * STREAM_WRAPPERS_ALL, which returns all registered stream wrappers. + * (Optional) Filters out all types except those with an on bit for each on + * bit in $filter. For example, if $filter is STREAM_WRAPPERS_WRITE_VISIBLE, + * which is equal to (STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE | + * STREAM_WRAPPERS_VISIBLE), then only stream wrappers with all three of these + * bits set are returned. Defaults to STREAM_WRAPPERS_ALL, which returns all + * registered stream wrappers. * * @return - * Returns the entire Drupal stream wrapper registry. + * An array keyed by scheme, with values containing an array of information + * about the stream wrapper, as returned by hook_stream_wrappers(). If $filter + * is omitted or set to STREAM_WRAPPERS_ALL, the entire Drupal stream wrapper + * registry is returned. Otherwise only the stream wrappers whose 'type' + * bitmask has an on bit for each bit specified in $filter are returned. * * @see hook_stream_wrappers() * @see hook_stream_wrappers_alter() @@ -122,7 +147,12 @@ function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) { else { $wrappers[$scheme]['override'] = FALSE; } - stream_wrapper_register($scheme, $info['class']); + if (($info['type'] & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) { + stream_wrapper_register($scheme, $info['class']); + } + else { + stream_wrapper_register($scheme, $info['class'], STREAM_IS_URL); + } } // Pre-populate the static cache with the filters most typically used. $wrappers_storage[STREAM_WRAPPERS_ALL][$scheme] = $wrappers[$scheme]; @@ -136,7 +166,7 @@ function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) { $wrappers_storage[$filter] = array(); foreach ($wrappers_storage[STREAM_WRAPPERS_ALL] as $scheme => $info) { // Bit-wise filter. - if ($info['type'] & $filter == $filter) { + if (($info['type'] & $filter) == $filter) { $wrappers_storage[$filter][$scheme] = $info; } } @@ -1185,6 +1215,7 @@ function file_delete(stdClass $file, $force = FALSE) { // Let other modules clean up any references to the deleted file. module_invoke_all('file_delete', $file); + module_invoke_all('entity_delete', $file, 'file'); // Make sure the file is deleted before removing its row from the // database, so UIs can still find the file in the database. @@ -2322,6 +2353,35 @@ function file_directory_temp() { return $temporary_directory; } +/** + * Examines a file object and returns appropriate content headers for download. + * + * @param $file + * A file object. + * @return + * An associative array of headers, as expected by file_transfer(). + */ +function file_get_content_headers($file) { + $name = mime_header_encode($file->filename); + $type = mime_header_encode($file->filemime); + // Serve images, text, and flash content for display rather than download. + $inline_types = variable_get('file_inline_types', array('^text/', '^image/', 'flash$')); + $disposition = 'attachment'; + foreach ($inline_types as $inline_type) { + // Exclamation marks are used as delimiters to avoid escaping slashes. + if (preg_match('!' . $inline_type . '!', $file->filemime)) { + $disposition = 'inline'; + } + } + + return array( + 'Content-Type' => $type . '; name="' . $name . '"', + 'Content-Length' => $file->filesize, + 'Content-Disposition' => $disposition . '; filename="' . $name . '"', + 'Cache-Control' => 'private', + ); +} + /** * @} End of "defgroup file". */ diff --git a/includes/form.inc b/includes/form.inc index 17ffe6b702079c672ed830fa5b3f030a36738803..6c4757f4373fe8e55622910b95123500f2ce0b43 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -1,5 +1,5 @@ <?php -// $Id: form.inc,v 1.501 2010/10/05 19:59:10 dries Exp $ +// $Id: form.inc,v 1.506 2010/10/21 20:46:58 webchick Exp $ /** * @defgroup forms Form builder functions @@ -163,7 +163,9 @@ * Any additional arguments are passed on to the functions called by * drupal_get_form(), including the unique form constructor function. For * example, the node_edit form requires that a node object is passed in here - * when it is called. + * when it is called. These are available to implementations of + * hook_form_alter() and hook_form_FORM_ID_alter() as the array + * $form_state['build_info']['args']. * * @return * The form array. @@ -1154,7 +1156,7 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) { $options = $elements['#options']; } if (is_array($elements['#value'])) { - $value = $elements['#type'] == 'checkboxes' ? array_keys($elements['#value']) : $elements['#value']; + $value = in_array($elements['#type'], array('checkboxes', 'tableselect')) ? array_keys($elements['#value']) : $elements['#value']; foreach ($value as $v) { if (!isset($options[$v])) { form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.')); @@ -1575,7 +1577,7 @@ function form_error(&$element, $message = '') { * was clicked when the form was submitted, as well as the sanitized * $_POST data. */ -function form_builder($form_id, $element, &$form_state) { +function form_builder($form_id, &$element, &$form_state) { // Initialize as unprocessed. $element['#processed'] = FALSE; @@ -1602,8 +1604,11 @@ function form_builder($form_id, $element, &$form_state) { $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action']; } - // Store a complete copy of the form in form_state prior to building the form. - $form_state['complete form'] = $element; + // Store a reference to the complete form in $form_state prior to building + // the form. This allows advanced #process and #after_build callbacks to + // perform changes elsewhere in the form. + $form_state['complete form'] = &$element; + // Set a flag if we have a correct form submission. This is always TRUE for // programmed forms coming from drupal_form_submit(), or if the form_id coming // from the POST data is set and matches the current form_id. @@ -1749,9 +1754,6 @@ function form_builder($form_id, $element, &$form_state) { // @todo Legacy support. Remove in Drupal 8. $form_state['clicked_button'] = $form_state['triggering_element']; } - - // Update the copy of the complete form for usage in validation handlers. - $form_state['complete form'] = $element; } return $element; } @@ -2120,6 +2122,41 @@ function form_type_checkboxes_value($element, $input = FALSE) { } } +/** + * Helper function to determine the value for a tableselect form element. + * + * @param $element + * The form element whose value is being populated. + * @param $input + * The incoming input to populate the form element. If this is FALSE, + * the element's default value should be returned. + * @return + * The data that will appear in the $element_state['values'] collection + * for this element. Return nothing to use the default. + */ +function form_type_tableselect_value($element, $input = FALSE) { + // If $element['#multiple'] == FALSE, then radio buttons are displayed and + // the default value handling is used. + if (isset($element['#multiple']) && $element['#multiple']) { + // Checkboxes are being displayed with the default value coming from the + // keys of the #default_value property. This differs from the checkboxes + // element which uses the array values. + if ($input === FALSE) { + $value = array(); + $element += array('#default_value' => array()); + foreach ($element['#default_value'] as $key => $flag) { + if ($flag) { + $value[$key] = $key; + } + } + return $value; + } + else { + return is_array($input) ? drupal_map_assoc($input) : array(); + } + } +} + /** * Helper function to determine the value for a password_confirm form * element. @@ -2656,19 +2693,24 @@ function form_process_date($element) { switch ($type) { case 'day': $options = drupal_map_assoc(range(1, 31)); + $title = t('Day'); break; case 'month': $options = drupal_map_assoc(range(1, 12), 'map_month'); + $title = t('Month'); break; case 'year': $options = drupal_map_assoc(range(1900, 2050)); + $title = t('Year'); break; } $element[$type] = array( '#type' => 'select', + '#title' => $title, + '#title_display' => 'invisible', '#value' => $element['#value'][$type], '#attributes' => $element['#attributes'], '#options' => $options, @@ -2932,12 +2974,12 @@ function theme_tableselect($variables) { $header = $element['#header']; if (!empty($element['#options'])) { // Generate a table row for each selectable item in #options. - foreach ($element['#options'] as $key => $value) { + foreach (element_children($element) as $key) { $row = array(); $row['data'] = array(); - if (isset($value['#attributes'])) { - $row += $value['#attributes']; + if (isset($element['#options'][$key]['#attributes'])) { + $row += $element['#options'][$key]['#attributes']; } // Render the checkbox / radio element. $row['data'][] = drupal_render($element[$key]); @@ -2991,8 +3033,6 @@ function form_process_tableselect($element) { $element['#default_value'] = array(); } - // Sort the options by their #weight if they have a #weight. - uasort($element['#options'], 'element_sort'); // Create a checkbox or radio for each item in #options in such a way that // the value of the tableselect element behaves as if it had been of type // checkboxes or radios. @@ -3000,9 +3040,16 @@ function form_process_tableselect($element) { // Do not overwrite manually created children. if (!isset($element[$key])) { if ($element['#multiple']) { + $title = ''; + if (!empty($element['#options'][$key]['title']['data']['#title'])) { + $title = t('Update @title', array( + '@title' => $element['#options'][$key]['title']['data']['#title'], + )); + } $element[$key] = array( '#type' => 'checkbox', - '#title' => '', + '#title' => $title, + '#title_display' => 'invisible', '#return_value' => $key, '#default_value' => isset($value[$key]) ? $key : NULL, '#attributes' => $element['#attributes'], @@ -3023,6 +3070,9 @@ function form_process_tableselect($element) { '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, ); } + if (isset($element['#options'][$key]['#weight'])) { + $element[$key]['#weight'] = $element['#options'][$key]['#weight']; + } } } } @@ -3032,6 +3082,130 @@ function form_process_tableselect($element) { return $element; } +/** + * Processes a machine-readable name form element. + * + * @param $element + * The form element to process. Properties used: + * - #machine_name: An associative array containing: + * - exists: A function name to invoke for checking whether a submitted + * machine name value already exists. The submitted value is passed as + * argument. In most cases, an existing API or menu argument loader + * function can be re-used. The callback is only invoked, if the submitted + * value differs from the element's #default_value. + * - source: (optional) The #array_parents of the form element containing + * the human-readable name (i.e., as contained in the $form structure) to + * use as source for the machine name. Defaults to array('name'). + * - label: (optional) A text to display as label for the machine name value + * after the human-readable name form element. Defaults to "Machine name". + * - replace_pattern: (optional) A regular expression (without delimiters) + * matching disallowed characters in the machine name. Defaults to + * '[^a-z0-9_]+'. + * - replace: (optional) A character to replace disallowed characters in the + * machine name via JavaScript. Defaults to '_' (underscore). When using a + * different character, 'replace_pattern' needs to be set accordingly. + * - error: (optional) A custom form error message string to show, if the + * machine name contains disallowed characters. + * - #maxlength: (optional) Should be set to the maximum allowed length of the + * machine name. Defaults to 64. + * - #disabled: (optional) Should be set to TRUE in case an existing machine + * name must not be changed after initial creation. + */ +function form_process_machine_name($element, &$form_state) { + // Apply default form element properties. + $element += array( + '#title' => t('Machine-readable name'), + '#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'), + '#machine_name' => array(), + ); + // A form element that only wants to set one #machine_name property (usually + // 'source' only) would leave all other properties undefined, if the defaults + // were defined in hook_element_info(). Therefore, we apply the defaults here. + $element['#machine_name'] += array( + 'source' => array('name'), + 'target' => '#' . $element['#id'], + 'label' => t('Machine name'), + 'replace_pattern' => '[^a-z0-9_]+', + 'replace' => '_', + ); + + // The source element defaults to array('name'), but may have been overidden. + if (empty($element['#machine_name']['source'])) { + return $element; + } + + // Retrieve the form element containing the human-readable name from the + // complete form in $form_state. By reference, because we need to append + // a #field_suffix that will hold the live preview. + $key_exists = NULL; + $source = drupal_array_get_nested_value($form_state['complete form'], $element['#machine_name']['source'], $key_exists); + if (!$key_exists) { + return $element; + } + + // Append a field suffix to the source form element, which will contain + // the live preview of the machine name. + $suffix_id = $source['#id'] . '-machine-name-suffix'; + $source += array('#field_suffix' => ''); + $source['#field_suffix'] .= ' <small id="' . $suffix_id . '"> </small>'; + + $parents = array_merge($element['#machine_name']['source'], array('#field_suffix')); + drupal_array_set_nested_value($form_state['complete form'], $parents, $source['#field_suffix']); + + $element['#machine_name']['suffix'] = '#' . $suffix_id; + + $js_settings = array( + 'type' => 'setting', + 'data' => array( + 'machineName' => array( + '#' . $source['#id'] => $element['#machine_name'], + ), + ), + ); + $element['#attached']['js'][] = 'misc/machine-name.js'; + $element['#attached']['js'][] = $js_settings; + + return $element; +} + +/** + * Form element validation handler for #type 'machine_name'. + * + * Note that #maxlength is validated by _form_validate() already. + */ +function form_validate_machine_name(&$element, &$form_state) { + // Verify that the machine name not only consists of replacement tokens. + if (preg_match('@^' . $element['#machine_name']['replace'] . '+$@', $element['#value'])) { + form_error($element, t('The machine-readable name must contain unique characters.')); + } + + // Verify that the machine name contains no disallowed characters. + if (preg_match('@' . $element['#machine_name']['replace_pattern'] . '@', $element['#value'])) { + if (!isset($element['#machine_name']['error'])) { + // Since a hyphen is the most common alternative replacement character, + // a corresponding validation error message is supported here. + if ($element['#machine_name']['replace'] == '-') { + form_error($element, t('The machine-readable name must contain only lowercase letters, numbers, and hyphens.')); + } + // Otherwise, we assume the default (underscore). + else { + form_error($element, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.')); + } + } + else { + form_error($element, $element['#machine_name']['error']); + } + } + + // Verify that the machine name is unique. + if ($element['#default_value'] !== $element['#value']) { + $function = $element['#machine_name']['exists']; + if ($function($element['#value'])) { + form_error($element, t('The machine-readable name is already in use. It must be unique.')); + } + } +} + /** * Adds fieldsets to the specified group or adds group members to this * fieldset. diff --git a/includes/install.core.inc b/includes/install.core.inc index fea10913d4ae2cc0eb5578686ce81adfc9845a11..92473c7844d5e45c66f9013fd0b006623455b955 100644 --- a/includes/install.core.inc +++ b/includes/install.core.inc @@ -1,5 +1,5 @@ <?php -// $Id: install.core.inc,v 1.38 2010/10/07 03:26:41 webchick Exp $ +// $Id: install.core.inc,v 1.40 2010/10/15 03:31:28 webchick Exp $ /** * @file @@ -819,7 +819,12 @@ function install_verify_settings() { * Verify PDO library. */ function install_verify_pdo() { - return extension_loaded('pdo'); + // PDO was moved to PHP core in 5.2.0, but the old extension (targeting 5.0 + // and 5.1) is still available from PECL, and can still be built without + // errors. To verify that the correct version is in use, we check the + // PDO::ATTR_DEFAULT_FETCH_MODE constant, which is not available in the + // PECL extension. + return extension_loaded('pdo') && defined('PDO::ATTR_DEFAULT_FETCH_MODE'); } /** @@ -1456,7 +1461,7 @@ function install_configure_form($form, &$form_state, &$install_state) { $settings_dir = './' . conf_path(); $settings_file = $settings_dir . '/settings.php'; if (!drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file($settings_dir, FILE_NOT_WRITABLE, 'dir')) { - drupal_set_message(st('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href="@handbook_url">online handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'error'); + drupal_set_message(st('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href="@handbook_url">online handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'warning'); } else { drupal_set_message(st('All necessary changes to %dir and %file have been made. They have been set to read-only for security.', array('%dir' => $settings_dir, '%file' => $settings_file))); diff --git a/includes/iso.inc b/includes/iso.inc index 7206497da35c452c9061a7d7a4729a51b8e53597..bf1a3fcabe0e13dafae7e52a089cff44483a21ec 100644 --- a/includes/iso.inc +++ b/includes/iso.inc @@ -1,5 +1,5 @@ <?php -// $Id: iso.inc,v 1.11 2010/06/23 19:06:10 dries Exp $ +// $Id: iso.inc,v 1.12 2010/10/09 18:20:43 webchick Exp $ /** * @file @@ -298,6 +298,7 @@ function _locale_get_predefined_list() { 'am' => array('Amharic', 'አማርኛ'), 'ar' => array('Arabic', /* Left-to-right marker "" */ 'العربية', LANGUAGE_RTL), 'as' => array('Assamese'), + 'ast' => array('Asturian'), 'av' => array('Avar'), 'ay' => array('Aymara'), 'az' => array('Azerbaijani', 'azərbaycan'), @@ -327,6 +328,7 @@ function _locale_get_predefined_list() { 'ee' => array('Ewe', 'Ɛʋɛ'), 'el' => array('Greek', 'Ελληνικά'), 'en' => array('English'), + 'en-gb' => array('English, British'), 'eo' => array('Esperanto'), 'es' => array('Spanish', 'Español'), 'et' => array('Estonian', 'Eesti'), @@ -334,6 +336,7 @@ function _locale_get_predefined_list() { 'fa' => array('Persian', /* Left-to-right marker "" */ 'فارسی', LANGUAGE_RTL), 'ff' => array('Fulah', 'Fulfulde'), 'fi' => array('Finnish', 'Suomi'), + 'fil' => array('Filipino'), 'fj' => array('Fiji'), 'fo' => array('Faeroese'), 'fr' => array('French', 'Français'), @@ -342,6 +345,7 @@ function _locale_get_predefined_list() { 'gd' => array('Scots Gaelic'), 'gl' => array('Galician', 'Galego'), 'gn' => array('Guarani'), + 'gsw-berne' => array('Swiss German'), 'gu' => array('Gujarati'), 'gv' => array('Manx'), 'ha' => array('Hausa'), @@ -349,6 +353,7 @@ function _locale_get_predefined_list() { 'hi' => array('Hindi', 'हिन्दी'), 'ho' => array('Hiri Motu'), 'hr' => array('Croatian', 'Hrvatski'), + 'ht' => array('Haitian Creole'), 'hu' => array('Hungarian', 'Magyar'), 'hy' => array('Armenian', 'Հայերեն'), 'hz' => array('Herero'), @@ -413,6 +418,7 @@ function _locale_get_predefined_list() { 'pi' => array('Pali'), 'pl' => array('Polish', 'Polski'), 'ps' => array('Pashto', /* Left-to-right marker "" */ 'پښتو', LANGUAGE_RTL), + 'pt' => array('Portuguese, International'), 'pt-pt' => array('Portuguese, Portugal', 'Português'), 'pt-br' => array('Portuguese, Brazil', 'Português'), 'qu' => array('Quechua'), @@ -423,6 +429,7 @@ function _locale_get_predefined_list() { 'rw' => array('Kinyarwanda'), 'sa' => array('Sanskrit'), 'sc' => array('Sardinian'), + 'sco' => array('Scots'), 'sd' => array('Sindhi'), 'se' => array('Northern Sami'), 'sg' => array('Sango'), @@ -462,6 +469,7 @@ function _locale_get_predefined_list() { 'vi' => array('Vietnamese', 'Tiếng Việt'), 'wo' => array('Wolof'), 'xh' => array('Xhosa', 'isiXhosa'), + 'xx-lolspeak' => array('Lolspeak'), 'yi' => array('Yiddish'), 'yo' => array('Yoruba', 'Yorùbá'), 'za' => array('Zhuang'), diff --git a/includes/menu.inc b/includes/menu.inc index f7217c62936d6bb5e9903bf3439c0189f2c48429..ecffedaa9d319415fdc878ba9ff86f4d96ee7fb4 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -1,5 +1,5 @@ <?php -// $Id: menu.inc,v 1.413 2010/10/03 23:29:09 dries Exp $ +// $Id: menu.inc,v 1.416 2010/10/21 11:56:17 dries Exp $ /** * @file @@ -72,7 +72,7 @@ */ /** - * @name Menu flags + * @defgroup menu_flags Menu flags * @{ * Flags for use in the "type" attribute of menu items. */ @@ -122,7 +122,7 @@ define('MENU_IS_LOCAL_ACTION', 0x0100); */ /** - * @name Menu item types + * @defgroup menu_item_types Menu item types * @{ * Menu item definitions provide one of these constants, which are shortcuts for * combinations of the above flags. @@ -185,7 +185,7 @@ define('MENU_LOCAL_ACTION', MENU_IS_LOCAL_TASK | MENU_IS_LOCAL_ACTION | MENU_VIS */ /** - * @name Menu context types + * @defgroup menu_context_types Menu context types * @{ * Flags for use in the "context" attribute of menu router items. */ @@ -213,7 +213,7 @@ define('MENU_CONTEXT_INLINE', 0x0002); */ /** - * @name Menu status codes + * @defgroup menu_status_codes Menu status codes * @{ * Status codes for menu callbacks. */ @@ -248,7 +248,7 @@ define('MENU_SITE_ONLINE', 5); */ /** - * @Name Menu tree parameters + * @defgroup menu_tree_parameters Menu tree parameters * @{ * Menu tree */ @@ -3464,7 +3464,6 @@ function _menu_router_build($callbacks) { 'page arguments' => array(), 'page callback' => '', 'delivery callback' => '', - 'block callback' => '', 'title arguments' => array(), 'title callback' => 't', 'theme arguments' => array(), @@ -3523,7 +3522,6 @@ function _menu_router_save($menu, $masks) { 'theme_callback', 'theme_arguments', 'type', - 'block_callback', 'description', 'position', 'weight', @@ -3554,7 +3552,6 @@ function _menu_router_save($menu, $masks) { 'theme_callback' => $item['theme callback'], 'theme_arguments' => serialize($item['theme arguments']), 'type' => $item['type'], - 'block_callback' => $item['block callback'], 'description' => $item['description'], 'position' => $item['position'], 'weight' => $item['weight'], diff --git a/includes/pager.inc b/includes/pager.inc index 8ca44a07ac54b3bd178e3484ba011ef1404479c1..22b3affeb20c358ccc00eb4d10d025a81e4352f4 100644 --- a/includes/pager.inc +++ b/includes/pager.inc @@ -1,5 +1,5 @@ <?php -// $Id: pager.inc,v 1.84 2010/10/06 13:38:39 dries Exp $ +// $Id: pager.inc,v 1.85 2010/10/08 05:07:53 webchick Exp $ /** * @file @@ -429,7 +429,7 @@ function theme_pager($variables) { /** - * @name Pager pieces + * @defgroup pagerpieces Pager pieces * @{ * Use these pieces to construct your own custom pagers in your theme. Note that * you should NOT modify this file to customize your pager. diff --git a/includes/session.inc b/includes/session.inc index 2a11b090fa26ceb05354b562fa7f968ceebe16ec..71e3f6c4ac8ed15d18aa0d48322c30b0c305f474 100644 --- a/includes/session.inc +++ b/includes/session.inc @@ -1,5 +1,5 @@ <?php -// $Id: session.inc,v 1.89 2010/09/17 14:56:27 dries Exp $ +// $Id: session.inc,v 1.90 2010/10/15 04:15:41 webchick Exp $ /** * @file @@ -112,13 +112,27 @@ function _drupal_session_read($sid) { $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; $user->roles += db_query("SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = :uid", array(':uid' => $user->uid))->fetchAllKeyed(0, 1); } - // We didn't find the client's record (session has expired), or they are - // blocked, or they are an anonymous user. + elseif ($user) { + // The user is anonymous or blocked. Only preserve two fields from the + // {sessions} table. + $account = drupal_anonymous_user(); + $account->session = $user->session; + $account->timestamp = $user->timestamp; + $user = $account; + } else { - $session = isset($user->session) ? $user->session : ''; - $user = drupal_anonymous_user($session); + // The session has expired. + $user = drupal_anonymous_user(); + $user->session = ''; } + // Store the session that was read for comparison in _drupal_session_write(). + $last_read = &drupal_static('drupal_session_last_read'); + $last_read = array( + 'sid' => $sid, + 'value' => $user->session, + ); + return $user->session; } @@ -142,44 +156,53 @@ function _drupal_session_read($sid) { function _drupal_session_write($sid, $value) { global $user, $is_https; - // The exception handler is not active at this point, so we need to do it manually. + // The exception handler is not active at this point, so we need to do it + // manually. try { if (!drupal_save_session()) { // We don't have anything to do if we are not allowed to save the session. return; } - // Either ssid or sid or both will be added from $key below. - $fields = array( - 'uid' => $user->uid, - 'cache' => isset($user->cache) ? $user->cache : 0, - 'hostname' => ip_address(), - 'session' => $value, - 'timestamp' => REQUEST_TIME, - ); - - // The "secure pages" setting allows a site to simultaneously use both secure - // and insecure session cookies. If enabled and both cookies are presented - // then use both keys. If not enabled but on HTTPS then use the PHP session - // id as 'ssid'. If on HTTP then use the PHP session id as 'sid'. - if ($is_https) { - $key['ssid'] = $sid; - $insecure_session_name = substr(session_name(), 1); - if (variable_get('https', FALSE) && isset($_COOKIE[$insecure_session_name])) { - $key['sid'] = $_COOKIE[$insecure_session_name]; + // Check whether $_SESSION has been changed in this request. + $last_read = &drupal_static('drupal_session_last_read'); + $is_changed = !isset($last_read) || $last_read['sid'] != $sid || $last_read['value'] !== $value; + + // For performance reasons, do not update the sessions table, unless + // $_SESSION has changed or more than 180 has passed since the last update. + if ($is_changed || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) { + // Either ssid or sid or both will be added from $key below. + $fields = array( + 'uid' => $user->uid, + 'cache' => isset($user->cache) ? $user->cache : 0, + 'hostname' => ip_address(), + 'session' => $value, + 'timestamp' => REQUEST_TIME, + ); + + // The "secure pages" setting allows a site to simultaneously use both + // secure and insecure session cookies. If enabled and both cookies are + // presented then use both keys. If not enabled but on HTTPS then use the + // PHP session id as 'ssid'. If on HTTP then use the PHP session id as + // 'sid'. + if ($is_https) { + $key['ssid'] = $sid; + $insecure_session_name = substr(session_name(), 1); + if (variable_get('https', FALSE) && isset($_COOKIE[$insecure_session_name])) { + $key['sid'] = $_COOKIE[$insecure_session_name]; + } + } + else { + $key['sid'] = $sid; } - } - else { - $key['sid'] = $sid; - } - db_merge('sessions') - ->key($key) - ->fields($fields) - ->execute(); + db_merge('sessions') + ->key($key) + ->fields($fields) + ->execute(); + } - // Last access time is updated no more frequently than once every 180 seconds. - // This reduces contention in the users table. + // Likewise, do not update access time more than once per 180 seconds. if ($user->uid && REQUEST_TIME - $user->access > variable_get('session_write_interval', 180)) { db_update('users') ->fields(array( @@ -193,7 +216,8 @@ function _drupal_session_write($sid, $value) { } catch (Exception $exception) { require_once DRUPAL_ROOT . '/includes/errors.inc'; - // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown. + // If we are displaying errors, then do so with no possibility of a further + // uncaught exception being thrown. if (error_displayable()) { print '<h1>Uncaught exception thrown in session handler.</h1>'; print '<p>' . _drupal_render_exception_safe($exception) . '</p><hr />'; @@ -203,7 +227,7 @@ function _drupal_session_write($sid, $value) { } /** - * Initialize the session handler, starting a session if needed. + * Initializes the session handler, starting a session if needed. */ function drupal_session_initialize() { global $user, $is_https; @@ -235,7 +259,7 @@ function drupal_session_initialize() { } /** - * Forcefully start a session, preserving already set session data. + * Forcefully starts a session, preserving already set session data. * * @ingroup php_wrappers */ @@ -256,7 +280,7 @@ function drupal_session_start() { } /** - * Commit the current session, if necessary. + * Commits the current session, if necessary. * * If an anonymous user already have an empty session, destroy it. */ @@ -287,7 +311,7 @@ function drupal_session_commit() { } /** - * Return whether a session has been started. + * Returns whether a session has been started. */ function drupal_session_started($set = NULL) { static $session_started = FALSE; @@ -365,7 +389,7 @@ function drupal_session_regenerate() { /** * Session handler assigned by session_set_save_handler(). * - * Cleanup a specific session. + * Cleans up a specific session. * * @param $sid * Session ID. @@ -407,7 +431,7 @@ function _drupal_session_delete_cookie($name, $force_insecure = FALSE) { } /** - * End a specific user's session(s). + * Ends a specific user's session(s). * * @param $uid * User ID. @@ -421,7 +445,7 @@ function drupal_session_destroy_uid($uid) { /** * Session handler assigned by session_set_save_handler(). * - * Cleanup stalled sessions. + * Cleans up stalled sessions. * * @param $lifetime * The value of session.gc_maxlifetime, passed by PHP. @@ -440,7 +464,7 @@ function _drupal_session_garbage_collection($lifetime) { } /** - * Determine whether to save session data of the current request. + * Determines whether to save session data of the current request. * * This function allows the caller to temporarily disable writing of * session data, should the request end while performing potentially diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc index 82f44d7a5696aa36b8a5c87e6060a44ba9f97cc4..c97f86bbc2dd5c2b3ab0dbd8d83a4b8cf4b6f878 100644 --- a/includes/stream_wrappers.inc +++ b/includes/stream_wrappers.inc @@ -1,5 +1,5 @@ <?php -// $Id: stream_wrappers.inc,v 1.20 2010/08/17 22:05:22 dries Exp $ +// $Id: stream_wrappers.inc,v 1.21 2010/10/21 12:09:41 dries Exp $ /** * @file @@ -22,6 +22,9 @@ /** * Stream wrapper bit flags that are the basis for composite types. + * + * Note that 0x0002 is skipped, because it was the value of a constant that has + * since been removed. */ /** @@ -34,11 +37,6 @@ define('STREAM_WRAPPERS_ALL', 0x0000); */ define('STREAM_WRAPPERS_LOCAL', 0x0001); -/** - * Stream wrapper bit flag -- refers to a remote filesystem location. - */ -define('STREAM_WRAPPERS_REMOTE', 0x0002); - /** * Stream wrapper bit flag -- wrapper is readable (almost always true). */ @@ -64,6 +62,11 @@ define('STREAM_WRAPPERS_VISIBLE', 0x0010); */ define('STREAM_WRAPPERS_HIDDEN', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_WRITE); +/** + * Stream wrapper type flag -- hidden, readable and writeable using local files. + */ +define('STREAM_WRAPPERS_LOCAL_HIDDEN', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_HIDDEN); + /** * Stream wrapper type flag -- visible, readable and writeable. */ @@ -74,10 +77,19 @@ define('STREAM_WRAPPERS_WRITE_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_W */ define('STREAM_WRAPPERS_READ_VISIBLE', STREAM_WRAPPERS_READ | STREAM_WRAPPERS_VISIBLE); +/** + * Stream wrapper type flag -- the default when 'type' is omitted from + * hook_stream_wrappers(). This does not include STREAM_WRAPPERS_LOCAL, + * because PHP grants a greater trust level to local files (for example, they + * can be used in an "include" statement, regardless of the "allow_url_include" + * setting), so stream wrappers need to explicitly opt-in to this. + */ +define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_WRITE_VISIBLE); + /** * Stream wrapper type flag -- visible, readable and writeable using local files. */ -define('STREAM_WRAPPERS_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_WRITE_VISIBLE); +define('STREAM_WRAPPERS_LOCAL_NORMAL', STREAM_WRAPPERS_LOCAL | STREAM_WRAPPERS_NORMAL); /** * Generic PHP stream wrapper interface. diff --git a/includes/tablesort.inc b/includes/tablesort.inc index fa7ed90927d164a086479600c57e287ad6742d1d..1242e8a87bb7eada78f7c521390b0ee994ab1a69 100644 --- a/includes/tablesort.inc +++ b/includes/tablesort.inc @@ -1,5 +1,5 @@ <?php -// $Id: tablesort.inc,v 1.58 2009/12/05 20:17:02 dries Exp $ +// $Id: tablesort.inc,v 1.59 2010/10/15 04:34:15 webchick Exp $ /** * @file @@ -128,12 +128,14 @@ class TableSort extends SelectQueryExtender { } else { // The first column specified is initial 'order by' field unless otherwise specified - if (is_array($this->header[0])) { - $this->header[0] += array('data' => NULL, 'field' => NULL); - return array('name' => $this->header[0]['data'], 'sql' => $this->header[0]['field']); + $headers = array_values($this->header); + $header = $headers[0]; + if (is_array($header)) { + $header += array('data' => NULL, 'field' => NULL); + return array('name' => $header['data'], 'sql' => $header['field']); } else { - return array('name' => $this->header[0]); + return array('name' => $header); } } } diff --git a/includes/theme.inc b/includes/theme.inc index de781acfda25ea414a662a52749272d3f61eed19..9db13f650b84afb3f9577c6d9e68ccd593affc48 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1,5 +1,5 @@ <?php -// $Id: theme.inc,v 1.618 2010/10/05 19:59:10 dries Exp $ +// $Id: theme.inc,v 1.620 2010/10/21 19:31:39 dries Exp $ /** * @file @@ -10,7 +10,7 @@ */ /** - * @name Content markers + * @defgroup content_flags Content markers * @{ * Markers used by theme_mark() and node_mark() to designate content. * @see theme_mark(), node_mark() @@ -108,7 +108,7 @@ function drupal_theme_initialize() { // @see ajax_base_page_theme() $setting['ajaxPageState'] = array( 'theme' => $theme_key, - 'themeToken' => drupal_get_token($theme_key), + 'theme_token' => drupal_get_token($theme_key), ); drupal_add_js($setting, 'setting'); } diff --git a/includes/token.inc b/includes/token.inc index 4eee5d24083fb2a6d8526dbbdb555a747a9bb1ff..8bd61e9afb07112ece6aa732d5da1215c3755350 100644 --- a/includes/token.inc +++ b/includes/token.inc @@ -1,5 +1,5 @@ <?php -// $Id: token.inc,v 1.9 2010/03/12 14:33:02 dries Exp $ +// $Id: token.inc,v 1.10 2010/10/18 01:13:07 dries Exp $ /** * @file @@ -152,17 +152,24 @@ function token_scan($text) { * An associative array of replacement values, keyed by the original 'raw' * tokens that were found in the source text. For example: * $results['[node:title]'] = 'My new node'; + * + * @see hook_tokens() + * @see hook_tokens_alter() */ function token_generate($type, array $tokens, array $data = array(), array $options = array()) { - $results = array(); $options += array('sanitize' => TRUE); + $replacements = module_invoke_all('tokens', $type, $tokens, $data, $options); - $result = module_invoke_all('tokens', $type, $tokens, $data, $options); - foreach ($result as $original => $replacement) { - $results[$original] = $replacement; - } + // Allow other modules to alter the replacements. + $context = array( + 'type' => $type, + 'tokens' => $tokens, + 'data' => $data, + 'options' => $options, + ); + drupal_alter('tokens', $replacements, $context); - return $results; + return $replacements; } /** diff --git a/includes/update.inc b/includes/update.inc index f68f0c5f3799855af9b480fecdbad7af2b0aad8a..3722c54fc6b7b28b8df4a1c2a8bf30b676f1dafa 100644 --- a/includes/update.inc +++ b/includes/update.inc @@ -1,5 +1,5 @@ <?php -// $Id: update.inc,v 1.76 2010/10/06 21:51:10 webchick Exp $ +// $Id: update.inc,v 1.78 2010/10/22 15:41:45 webchick Exp $ /** * @file @@ -113,16 +113,27 @@ function update_prepare_d7_bootstrap() { // loaded. Bootstrapping to DRUPAL_BOOTSTRAP_DATABASE will result in a fatal // error otherwise. $message = ''; + $pdo_link = 'http://drupal.org/requirements/pdo'; // Check that PDO is loaded. if (!extension_loaded('pdo')) { $message = '<h2>PDO is required!</h2><p>Drupal 7 requires PHP ' . DRUPAL_MINIMUM_PHP . ' or higher with the PHP Data Objects (PDO) extension enabled.</p>'; } + // The PDO::ATTR_DEFAULT_FETCH_MODE constant is not available in the PECL + // version of PDO. + elseif (!defined('PDO::ATTR_DEFAULT_FETCH_MODE')) { + $message = '<h2>The wrong version of PDO is installed!</h2><p>Drupal 7 requires the PHP Data Objects (PDO) extension from PHP core to be enabled. This system has the older PECL version installed.'; + $pdo_link = 'http://drupal.org/requirements/pdo#pecl'; + } // Check that the correct driver is loaded for the database being updated. - elseif (!in_array($databases['default']['default']['driver'], PDO::getAvailableDrivers())) { - $message = '<h2>A PDO database driver is required!</h2><p>You need to enable the PDO_' . strtoupper($databases['default']['default']['driver']) . ' database driver for PHP ' . DRUPAL_MINIMUM_PHP . ' or higher so that Drupal 7 can access the database.</p>'; + // If we have no driver information (for example, if someone tried to create + // the Drupal 7 $databases array themselves but did not do it correctly), + // this message will be confusing, so do not perform the check; instead, just + // let the database connection fail in the code that follows. + elseif (isset($databases['default']['default']['driver']) && !in_array($databases['default']['default']['driver'], PDO::getAvailableDrivers())) { + $message = '<h2>A PDO database driver is required!</h2><p>You need to enable the PDO_' . strtoupper($databases['default']['default']['driver']) . ' database driver for PHP ' . DRUPAL_MINIMUM_PHP . ' or higher so that Drupal 7 can access the database.</p>'; } if ($message) { - print $message . '<p>See the <a href="http://drupal.org/requirements">system requirements page</a> for more information.</p>'; + print $message . '<p>See the <a href="' . $pdo_link . '">system requirements page</a> for more information.</p>'; exit(); } diff --git a/install.php b/install.php index 210c313d3938688307da4b80ebd70dfd235d5c32..4b6c677c839659884e9b387431a5fa0d5575945c 100644 --- a/install.php +++ b/install.php @@ -1,5 +1,5 @@ <?php -// $Id: install.php,v 1.237 2010/02/17 04:19:51 webchick Exp $ +// $Id: install.php,v 1.238 2010/10/22 02:53:19 webchick Exp $ /** * @file @@ -16,6 +16,12 @@ define('DRUPAL_ROOT', getcwd()); */ define('MAINTENANCE_MODE', 'install'); +// Exit early if running an incompatible PHP version to avoid fatal errors. +if (version_compare(PHP_VERSION, '5.2.4') < 0) { + print 'Your PHP installation is too old. Drupal requires at least PHP 5.2.4. See the <a href="http://drupal.org/requirements">system requirements</a> page for more information.'; + exit; +} + // Start the installer. require_once DRUPAL_ROOT . '/includes/install.core.inc'; install_drupal(); diff --git a/misc/ajax.js b/misc/ajax.js index 01a90ef116dc4f284f197d54fda5588ed07b6d6c..dbbce837d3a59bbb9cc7d4949e73e1fea6a784f8 100644 --- a/misc/ajax.js +++ b/misc/ajax.js @@ -1,4 +1,4 @@ -// $Id: ajax.js,v 1.23 2010/10/06 18:27:09 webchick Exp $ +// $Id: ajax.js,v 1.25 2010/10/21 19:31:39 dries Exp $ (function ($) { /** @@ -176,6 +176,7 @@ Drupal.ajax = function (base, element, element_settings) { ajax.form.ajaxSubmit(ajax.options); } else { + ajax.beforeSerialize(ajax.element, ajax.options); $.ajax(ajax.options); } } @@ -216,31 +217,35 @@ Drupal.ajax.prototype.beforeSerialize = function (element, options) { var settings = this.settings || Drupal.settings; Drupal.detachBehaviors(this.form, settings, 'serialize'); } -}; - -/** - * Handler for the form redirection submission. - */ -Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) { - // Disable the element that received the change. - $(this.element).addClass('progress-disabled').attr('disabled', true); // Prevent duplicate HTML ids in the returned markup. // @see drupal_html_id() + options.data['ajax_html_ids[]'] = []; $('[id]').each(function () { - form_values.push({ name: 'ajax_html_ids[]', value: this.id }); + options.data['ajax_html_ids[]'].push(this.id); }); // Allow Drupal to return new JavaScript and CSS files to load without // returning the ones already loaded. - form_values.push({ name: 'ajax_page_state[theme]', value: Drupal.settings.ajaxPageState.theme }); - form_values.push({ name: 'ajax_page_state[theme_token]', value: Drupal.settings.ajaxPageState.themeToken }); + // @see ajax_base_page_theme() + // @see drupal_get_css() + // @see drupal_get_js() + options.data['ajax_page_state[theme]'] = Drupal.settings.ajaxPageState.theme; + options.data['ajax_page_state[theme_token]'] = Drupal.settings.ajaxPageState.theme_token; for (var key in Drupal.settings.ajaxPageState.css) { - form_values.push({ name: 'ajax_page_state[css][' + key + ']', value: 1 }); + options.data['ajax_page_state[css][' + key + ']'] = 1; } for (var key in Drupal.settings.ajaxPageState.js) { - form_values.push({ name: 'ajax_page_state[js][' + key + ']', value: 1 }); + options.data['ajax_page_state[js][' + key + ']'] = 1; } +}; + +/** + * Handler for the form redirection submission. + */ +Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) { + // Disable the element that received the change. + $(this.element).addClass('progress-disabled').attr('disabled', true); // Insert progressbar or throbber. if (this.progress.type == 'bar') { @@ -279,7 +284,7 @@ Drupal.ajax.prototype.success = function (response, status) { Drupal.freezeHeight(); - for (i in response) { + for (var i in response) { if (response[i]['command'] && this.commands[response[i]['command']]) { this.commands[response[i]['command']](this, response[i], status); } @@ -298,7 +303,7 @@ Drupal.ajax.prototype.success = function (response, status) { // Remove any response-specific settings so they don't get used on the next // call by mistake. - this.settings = {}; + this.settings = null; }; /** diff --git a/misc/machine-name.js b/misc/machine-name.js new file mode 100644 index 0000000000000000000000000000000000000000..0e7870e6cf152922a2e1cc2997831dcc71c733b0 --- /dev/null +++ b/misc/machine-name.js @@ -0,0 +1,110 @@ +// $Id: machine-name.js,v 1.1 2010/10/13 17:09:00 webchick Exp $ +(function ($) { + +/** + * Attach the machine-readable name form element behavior. + */ +Drupal.behaviors.machineName = { + /** + * Attaches the behavior. + * + * @param settings.machineName + * A list of elements to process, keyed by the HTML ID of the form element + * containing the human-readable value. Each element is an object defining + * the following properties: + * - target: The HTML ID of the machine name form element. + * - suffix: The HTML ID of a container to show the machine name preview in + * (usually a field suffix after the human-readable name form element). + * - label: The label to show for the machine name preview. + * - replace_pattern: A regular expression (without modifiers) matching + * disallowed characters in the machine name; e.g., '[^a-z0-9]+'. + * - replace: A character to replace disallowed characters with; e.g., '_' + * or '-'. + */ + attach: function (context, settings) { + var self = this; + $.each(settings.machineName, function (source_id, options) { + var $source = $(source_id, context).addClass('machine-name-source'); + var $target = $(options.target, context).addClass('machine-name-target'); + var $suffix = $(options.suffix, context); + var $wrapper = $target.parents('.form-item:first'); + // All elements have to exist. + if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) { + return; + } + // Skip processing upon a form validation error on the machine name. + if ($target.hasClass('error')) { + return; + } + // Hide the form item container of the machine name form element. + $wrapper.hide(); + // Append the machine name preview to the source field. + if ($target.is(':disabled')) { + var machine = $target.val(); + } + else { + var machine = self.transliterate($source.val(), options); + } + var $preview = $('<span class="machine-name-value">' + machine + '</span>'); + $suffix.empty() + .append(' ').append('<span class="machine-name-label">' + options.label + ':</span>') + .append(' ').append($preview); + + // Append a link to edit the machine name, if it is editable, and only if + // one of the following conditions is met: + // - the machine name is empty. + // - the human-readable name is equal to the existing machine name. + if (!$target.is(':disabled') && ($target.val() == '' || $target.val() == machine)) { + var $link = $('<span class="admin-link"><a href="#">' + Drupal.t('Edit') + '</a></span>') + .click(function () { + $wrapper.show(); + $target.focus(); + $suffix.hide(); + $source.unbind('keyup.machineName'); + return false; + }); + $suffix.append(' ').append($link); + + // Preview machine name in realtime when the human-readable name changes. + $source.bind('keyup.machineName change.machineName', function () { + machine = self.transliterate($(this).val(), options); + // Set the machine name to the transliterated value. + if (machine != options.replace && machine != '') { + $target.val(machine); + $preview.text(machine); + $suffix.show(); + } + else { + $suffix.hide(); + $target.val(machine); + $preview.empty(); + } + }); + // Initialize machine name preview. + $source.keyup(); + } + }); + }, + + /** + * Transliterate a human-readable name to a machine name. + * + * @param source + * A string to transliterate. + * @param settings + * The machine name settings for the corresponding field, containing: + * - replace_pattern: A regular expression (without modifiers) matching + * disallowed characters in the machine name; e.g., '[^a-z0-9]+'. + * - replace: A character to replace disallowed characters with; e.g., '_' + * or '-'. + * + * @return + * The transliterated source string. + */ + transliterate: function (source, settings) { + var rx = new RegExp(settings.replace_pattern, 'g'); + return source.toLowerCase().replace(rx, settings.replace); + } +}; + +})(jQuery); diff --git a/misc/vertical-tabs-rtl.css b/misc/vertical-tabs-rtl.css index c44a1403c03dae8dd3762031cdf02279a8f2c0a5..5dc8c9895192503b035f9b28a0c8bab541518cb7 100644 --- a/misc/vertical-tabs-rtl.css +++ b/misc/vertical-tabs-rtl.css @@ -1,15 +1,15 @@ -/* $Id: vertical-tabs-rtl.css,v 1.2 2009/05/19 06:03:25 dries Exp $ */ +/* $Id: vertical-tabs-rtl.css,v 1.3 2010/10/07 15:54:13 webchick Exp $ */ -.vertical-tabs { +div.vertical-tabs { margin-left: 0; margin-right: 15em; } -.vertical-tabs-list { +.vertical-tabs ul.vertical-tabs-list { + margin-left: 0; margin-right: -15em; - right: 0; float: right; } -.vertical-tabs-list li.selected { +.vertical-tabs ul.vertical-tabs-list li.selected { border-left-width: 0; border-right-width: 1px; } diff --git a/misc/vertical-tabs.css b/misc/vertical-tabs.css index d0312d6a7c7d92d11a76da4b3601bd3eeb57f288..d350c8e6267a2ae03cd78af20c2f7bd44cc0498d 100644 --- a/misc/vertical-tabs.css +++ b/misc/vertical-tabs.css @@ -1,70 +1,78 @@ -/* $Id: vertical-tabs.css,v 1.7 2010/03/30 07:05:58 webchick Exp $ */ +/* $Id: vertical-tabs.css,v 1.8 2010/10/07 15:54:13 webchick Exp $ */ div.vertical-tabs { - margin: 1em 0 1em 15em; + margin: 1em 0 1em 15em; /* LTR */ border: 1px solid #ccc; + position: relative; /* IE6/7 */ } -div.vertical-tabs ul.vertical-tabs-list { +.vertical-tabs ul.vertical-tabs-list { width: 15em; list-style: none; list-style-image: none; /* IE6 */ border-top: 1px solid #ccc; padding: 0; position: relative; /* IE6 */ - margin: -1px -100% -1px 0; - left: -15em; - float: left; + margin: -1px 0 -1px -15em; /* LTR */ + float: left; /* LTR */ } -div.vertical-tabs .vertical-tabs-panes fieldset.vertical-tabs-pane { +.vertical-tabs fieldset.vertical-tabs-pane { margin: 0 !important; padding: 0 1em; border: 0; } -div.vertical-tabs .vertical-tabs-panes fieldset.vertical-tabs-pane legend { +.vertical-tabs legend { display: none; } /* Layout of each tab */ -div.vertical-tabs ul.vertical-tabs-list li { +.vertical-tabs ul.vertical-tabs-list li { background: #eee; border: 1px solid #ccc; border-top: 0; padding: 0; margin: 0; - height: 1%; + min-width: 0; /* IE7 */ } -div.vertical-tabs ul.vertical-tabs-list li a { +.vertical-tabs ul.vertical-tabs-list li a { display: block; text-decoration: none; padding: 0.5em 0.6em; - line-height: 1.3em; - height: 1%; } -div.vertical-tabs ul.vertical-tabs-list li a:focus strong, -div.vertical-tabs ul.vertical-tabs-list li a:active strong, -div.vertical-tabs ul.vertical-tabs-list li a:hover strong { +.vertical-tabs ul.vertical-tabs-list li a:focus strong, +.vertical-tabs ul.vertical-tabs-list li a:active strong, +.vertical-tabs ul.vertical-tabs-list li a:hover strong { text-decoration: underline; } -div.vertical-tabs ul.vertical-tabs-list li a:focus, -div.vertical-tabs ul.vertical-tabs-list li a:active { - position: relative; - z-index: 5; -} -div.vertical-tabs ul.vertical-tabs-list li a:hover { +.vertical-tabs ul.vertical-tabs-list li a:hover { outline: 1px dotted; } -div.vertical-tabs ul.vertical-tabs-list li.selected { +.vertical-tabs ul.vertical-tabs-list li.selected { background-color: #fff; - border-right-width: 0; - position: relative; + border-right-width: 0; /* LTR */ } -div.vertical-tabs ul.vertical-tabs-list span.selected strong { +.vertical-tabs ul.vertical-tabs-list .selected strong { color: #000; } -div.vertical-tabs ul.vertical-tabs-list .summary { +.vertical-tabs ul.vertical-tabs-list .summary { display: block; } -div.vertical-tabs ul.vertical-tabs ul.vertical-tabs-list .summary { +.vertical-tabs ul.vertical-tabs ul.vertical-tabs-list .summary { line-height: normal; margin-bottom: 0; } + +/** + * Prevent text inputs from overflowing when container is too narrow. "width" is + * applied to override hardcoded cols or size attributes and used in conjunction + * with "box-sizing" to prevent box model issues from occurring in most browsers. +*/ +.vertical-tabs .form-type-textfield input { + width: 100%; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +* html .vertical-tabs .form-type-textfield, +* html .vertical-tabs .form-textarea-wrapper { + width: 95%; /* IE6 */ +} diff --git a/modules/aggregator/aggregator-summary-items.tpl.php b/modules/aggregator/aggregator-summary-items.tpl.php index a8b6b7bd0006f023927a1a24cb605ba85c403d2d..eeb259822ae4f2db24a9e03b544fbe12d97536b7 100644 --- a/modules/aggregator/aggregator-summary-items.tpl.php +++ b/modules/aggregator/aggregator-summary-items.tpl.php @@ -1,5 +1,5 @@ <?php -// $Id: aggregator-summary-items.tpl.php,v 1.3 2008/08/03 16:10:50 dries Exp $ +// $Id: aggregator-summary-items.tpl.php,v 1.4 2010/10/15 23:37:55 webchick Exp $ /** * @file @@ -14,7 +14,7 @@ * - $source_url: URL to the local source or category. * * @see template_preprocess() - * @see template_preprocess_aggregator_summary-items() + * @see template_preprocess_aggregator_summary_items() */ ?> <h3><?php print $title; ?></h3> diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info index 6134004950aed4d1c05a4575d6be7a16970cfb5a..47a90f24eb4d5fed90ddbe2ef27a262ae7f6b362 100644 --- a/modules/aggregator/aggregator.info +++ b/modules/aggregator/aggregator.info @@ -15,8 +15,8 @@ files[] = aggregator.test configure = admin/config/services/aggregator/settings stylesheets[all][] = aggregator.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc index 0f5e850c2609f25651b3e5ac04a934b090205dbf..08c2de7cf356ea1779472262eca46048f6817a3e 100644 --- a/modules/aggregator/aggregator.pages.inc +++ b/modules/aggregator/aggregator.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: aggregator.pages.inc,v 1.44 2010/10/06 13:38:39 dries Exp $ +// $Id: aggregator.pages.inc,v 1.45 2010/10/15 23:37:55 webchick Exp $ /** * @file @@ -471,7 +471,7 @@ function theme_aggregator_page_opml($variables) { /** * Process variables for aggregator-summary-items.tpl.php. * - * @see aggregator-summary-item.tpl.php + * @see aggregator-summary-items.tpl.php */ function template_preprocess_aggregator_summary_items(&$variables) { $variables['title'] = check_plain($variables['source']->title); diff --git a/modules/aggregator/tests/aggregator_test.info b/modules/aggregator/tests/aggregator_test.info index 065cebd48d310ac459db1ee10f7f5b7d3d6152bf..50b8b93b324c96d8f0eb3400322d501921d3d92f 100644 --- a/modules/aggregator/tests/aggregator_test.info +++ b/modules/aggregator/tests/aggregator_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = aggregator_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index 4a51eaee014a5498c15dad38e98b93c8617d9603..06111cc3515d88b46efea04497a42457ad9ea80b 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: block.admin.inc,v 1.89 2010/10/04 18:00:45 dries Exp $ +// $Id: block.admin.inc,v 1.90 2010/10/10 20:11:21 dries Exp $ /** * @file @@ -148,8 +148,10 @@ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_r ); } } - // Do not allow disabling the main system content block. - $form['blocks']['system_main']['region']['#required'] = TRUE; + // Do not allow disabling the main system content block when it is present. + if (isset($form['blocks']['system_main']['region'])) { + $form['blocks']['system_main']['region']['#required'] = TRUE; + } $form['actions'] = array( '#tree' => FALSE, diff --git a/modules/block/block.info b/modules/block/block.info index 5077c9f1149c092fae7c0aaf474134e9f19f1357..381c0bdc357c1b68362c3491204c11257a1fc437 100644 --- a/modules/block/block.info +++ b/modules/block/block.info @@ -10,8 +10,8 @@ files[] = block.install files[] = block.test configure = admin/structure/block -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/block/block.install b/modules/block/block.install index a21e8d2b16ed7161f7fea1621cbaa32ca8d63b5d..95f74c42d9aac126c2dbd3eb0fd8d604ebb1136e 100644 --- a/modules/block/block.install +++ b/modules/block/block.install @@ -1,5 +1,5 @@ <?php -// $Id: block.install,v 1.46 2010/09/28 03:30:37 webchick Exp $ +// $Id: block.install,v 1.47 2010/10/20 01:15:58 dries Exp $ /** * @file @@ -155,8 +155,8 @@ function block_schema() { 'description' => 'Block description.', ), 'format' => array( - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 255, 'not null' => FALSE, 'description' => 'The {filter_format}.format of the block body.', ), @@ -196,6 +196,11 @@ function block_update_dependencies() { $dependencies['block'][7005] = array( 'filter' => 7000, ); + // Ensure that format columns are only changed after Filter module has changed + // the primary records. + $dependencies['block'][7007] = array( + 'filter' => 7010, + ); return $dependencies; } @@ -444,6 +449,18 @@ function block_update_7006() { db_create_table('cache_block', $schema); } +/** + * Change {block_custom}.format into varchar. + */ +function block_update_7007() { + db_change_field('block_custom', 'format', 'format', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'The {filter_format}.format of the block body.', + )); +} + /** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/block/block.test b/modules/block/block.test index db8a257f97de27c7b6032b525b5dfde7a2af02cd..863c6979e3567bb6715c6c9be8dfb86ec8d97e7d 100644 --- a/modules/block/block.test +++ b/modules/block/block.test @@ -1,5 +1,5 @@ <?php -// $Id: block.test,v 1.58 2010/08/30 00:22:03 webchick Exp $ +// $Id: block.test,v 1.59 2010/10/21 19:31:39 dries Exp $ /** * @file @@ -87,8 +87,8 @@ class BlockTestCase extends DrupalWebTestCase { // Verify presence of configure and delete links for custom block. $this->drupalGet('admin/structure/block'); - $this->assertRaw(l(t('configure'), 'admin/structure/block/manage/block/' . $bid . '/configure'), t('Custom block configure link found.')); - $this->assertRaw(l(t('delete'), 'admin/structure/block/manage/block/' . $bid . '/delete'), t('Custom block delete link found.')); + $this->assertLinkByHref('admin/structure/block/manage/block/' . $bid . '/configure', 0, t('Custom block configure link found.')); + $this->assertLinkByHref('admin/structure/block/manage/block/' . $bid . '/delete', 0, t('Custom block delete link found.')); // Set visibility only for authenticated users, to verify delete functionality. $edit = array(); diff --git a/modules/block/tests/block_test.info b/modules/block/tests/block_test.info index a941f84a2a860963d56d49851893b3332dd95be9..38c8d06fdcf5f89028da29d0c5652330a0b853e1 100644 --- a/modules/block/tests/block_test.info +++ b/modules/block/tests/block_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = block_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/blog/blog.info b/modules/blog/blog.info index 491fe0bf30a5e153227e754c82e1bfe15524f0b2..2de09d1c0c8e4d249cece9993474f3afd43ba3ed 100644 --- a/modules/blog/blog.info +++ b/modules/blog/blog.info @@ -9,8 +9,8 @@ files[] = blog.module files[] = blog.pages.inc files[] = blog.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/book/book.admin.inc b/modules/book/book.admin.inc index b589f7d11ed0495eda391eedde8a4b9d1dc086f8..41b4aaa15ea53b92f0d0f8d3008fadd84e5d6d5f 100644 --- a/modules/book/book.admin.inc +++ b/modules/book/book.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: book.admin.inc,v 1.37 2010/09/27 00:53:56 dries Exp $ +// $Id: book.admin.inc,v 1.38 2010/10/20 01:31:06 dries Exp $ /** * @file @@ -191,11 +191,12 @@ function _book_admin_table_tree($tree, &$form) { '#type' => 'weight', '#default_value' => $data['link']['weight'], '#delta' => 15, + '#title' => t('Weight for @title', array('@title' => $data['link']['title'])), + '#title_display' => 'invisible', ), 'plid' => array( - '#type' => 'textfield', + '#type' => 'hidden', '#default_value' => $data['link']['plid'], - '#size' => 6, ), 'mlid' => array( '#type' => 'hidden', diff --git a/modules/book/book.info b/modules/book/book.info index c0a360b0050dd039db7f3a52aa08a88b69ca9699..0a1079da31691723f7f8b4de84d80750d038b8cc 100644 --- a/modules/book/book.info +++ b/modules/book/book.info @@ -12,8 +12,8 @@ files[] = book.test configure = admin/content/book/settings stylesheets[all][] = book.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/book/book.module b/modules/book/book.module index 5baa788872994095acc79796f96bc107e0279b22..18194f64b81e98dbb55c4fc52e1af2ffa513212e 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -1,5 +1,5 @@ <?php -// $Id: book.module,v 1.553 2010/09/24 00:37:42 dries Exp $ +// $Id: book.module,v 1.554 2010/10/18 05:53:34 webchick Exp $ /** * @file @@ -264,6 +264,9 @@ function book_block_view($delta = '') { // Since we know we will only display a link to the top node, there // is no reason to run an additional menu tree query for each book. $book['in_active_trail'] = FALSE; + // Check whether user can access the book link. + $book_node = node_load($book['nid']); + $book['access'] = node_access('view', $book_node); $pseudo_tree[0]['link'] = $book; $book_menus[$book_id] = menu_tree_output($pseudo_tree); } diff --git a/modules/book/book.test b/modules/book/book.test index c683bd5afa574963a5412fba4201bfd24cb198e7..7f8c8160c85b68a417a7219c1f2ee789dc2eb033 100644 --- a/modules/book/book.test +++ b/modules/book/book.test @@ -1,14 +1,16 @@ <?php -// $Id: book.test,v 1.27 2010/08/30 00:22:03 webchick Exp $ +// $Id: book.test,v 1.28 2010/10/18 05:53:34 webchick Exp $ class BookTestCase extends DrupalWebTestCase { protected $book; - // $book_author is a user with permission to author a book. + // $book_author is a user with permission to create and edit books. protected $book_author; - // $web_user is a user with permission to view a book + // $web_user is a user with permission to view a book // and access the printer-friendly version. protected $web_user; - + // $admin_user is a user with permission to create and edit books and to administer blocks. + protected $admin_user; + public static function getInfo() { return array( 'name' => 'Book functionality', @@ -19,12 +21,13 @@ class BookTestCase extends DrupalWebTestCase { function setUp() { parent::setUp('book'); - + // Create users. $this->book_author = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books')); $this->web_user = $this->drupalCreateUser(array('access printer-friendly version')); + $this->admin_user = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks')); } - + /** * Create a new book with a page hierarchy. */ @@ -52,7 +55,7 @@ class BookTestCase extends DrupalWebTestCase { $nodes[] = $this->createBookNode($book->nid); // Node 4. $this->drupalLogout(); - + return $nodes; } @@ -63,7 +66,7 @@ class BookTestCase extends DrupalWebTestCase { // Create new book. $nodes = $this->createBook(); $book = $this->book; - + $this->drupalLogin($this->web_user); // Check that book pages display along with the correct outlines and @@ -211,68 +214,63 @@ class BookTestCase extends DrupalWebTestCase { return $node; } - + /** * Tests book export ("printer-friendly version") functionality. */ function testBookExport() { // Create a book. $nodes = $this->createBook(); - + // Login as web user and view printer-friendly version. $this->drupalLogin($this->web_user); $this->drupalGet('node/' . $this->book->nid); $this->clickLink(t('Printer-friendly version')); - + // Make sure each part of the book is there. foreach ($nodes as $node) { $this->assertText($node->title, t('Node title found in printer friendly version.')); $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Node body found in printer friendly version.')); } - + // Make sure we can't export an unsupported format. $this->drupalGet('book/export/foobar/' . $this->book->nid); - $this->assertResponse('404', t('Unsupported export format returned "not found".')); - + $this->assertResponse('404', t('Unsupported export format returned "not found".')); + // Make sure an anonymous user cannot view printer-friendly version. $this->drupalLogout(); - + // Load the book and verify there is no printer-friendly version link. $this->drupalGet('node/' . $this->book->nid); $this->assertNoLink(t('Printer-friendly version'), t('Anonymous user is not shown link to printer-friendly version.')); - + // Try getting the URL directly, and verify it fails. $this->drupalGet('book/export/html/' . $this->book->nid); $this->assertResponse('403', t('Anonymous user properly forbidden.')); } -} - -class BookBlockTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Block availability', - 'description' => 'Check if the book navigation block is available.', - 'group' => 'Book', - ); - } - - function setUp() { - parent::setUp('book'); - - // Create and login user - $admin_user = $this->drupalCreateUser(array('administer blocks')); - $this->drupalLogin($admin_user); - } + /** + * Tests the functionality of the book navigation block. + */ function testBookNavigationBlock() { - // Set block title to confirm that the interface is availble. - $this->drupalPost('admin/structure/block/manage/book/navigation/configure', array('title' => $this->randomName(8)), t('Save block')); + $this->drupalLogin($this->admin_user); + + // Set block title to confirm that the interface is available. + $block_title = $this->randomName(16); + $this->drupalPost('admin/structure/block/manage/book/navigation/configure', array('title' => $block_title), t('Save block')); $this->assertText(t('The block configuration has been saved.'), t('Block configuration set.')); - // Set the block to a region to confirm block is availble. + // Set the block to a region to confirm block is available. $edit = array(); $edit['blocks[book_navigation][region]'] = 'footer'; $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); $this->assertText(t('The block settings have been updated.'), t('Block successfully move to footer region.')); + + // Test correct display of the block. + $nodes = $this->createBook(); + $this->drupalGet('<front>'); + $this->assertText($block_title, t('Book navigation block is displayed.')); + $this->assertText($this->book->title, t('Link to book root (@title) is displayed.', array('@title' => $nodes[0]->title))); + $this->assertNoText($nodes[0]->title, t('No links to individual book pages are displayed.')); } } diff --git a/modules/color/color.info b/modules/color/color.info index f67f4d1a9ac80d41ab92b35d8274c7848ff9f639..69d711755e29916b59263910ce4e1776668e596f 100644 --- a/modules/color/color.info +++ b/modules/color/color.info @@ -9,8 +9,8 @@ files[] = color.module files[] = color.install files[] = color.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/color/color.install b/modules/color/color.install index 37614f8532fe00d783c0f42fe74e811a4550c82c..5ecf551bd371f8ee1d2b9a81faeb610c588d3e5a 100644 --- a/modules/color/color.install +++ b/modules/color/color.install @@ -1,11 +1,14 @@ <?php -/* $Id: color.install,v 1.7 2010/04/04 13:18:18 dries Exp $ */ +/* $Id: color.install,v 1.8 2010/10/15 23:41:53 webchick Exp $ */ /** * @file * Install, update and uninstall functions for the color module. */ +/** + * Implements hook_requirements(). + */ function color_requirements($phase) { $requirements = array(); diff --git a/modules/comment/comment.admin.inc b/modules/comment/comment.admin.inc index 674053c995800242696d91295ee3fca0ddad8a4c..0e6a58fe18a623c06c62f4d7196802acbb327492 100644 --- a/modules/comment/comment.admin.inc +++ b/modules/comment/comment.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: comment.admin.inc,v 1.49 2010/09/11 01:47:14 dries Exp $ +// $Id: comment.admin.inc,v 1.51 2010/10/20 01:31:06 dries Exp $ /** * @file @@ -50,6 +50,8 @@ function comment_admin_overview($form, &$form_state, $arg) { $form['options']['operation'] = array( '#type' => 'select', + '#title' => t('Operation'), + '#title_display' => 'invisible', '#options' => $options, '#default_value' => 'publish', ); @@ -64,7 +66,7 @@ function comment_admin_overview($form, &$form_state, $arg) { 'subject' => array('data' => t('Subject'), 'field' => 'subject'), 'author' => array('data' => t('Author'), 'field' => 'name'), 'posted_in' => array('data' => t('Posted in'), 'field' => 'node_title'), - 'changed' => array('data' => t('Updated'), 'field' => 'changed', 'sort' => 'desc'), + 'changed' => array('data' => t('Updated'), 'field' => 'c.changed', 'sort' => 'desc'), 'operations' => array('data' => t('Operations')), ); diff --git a/modules/comment/comment.info b/modules/comment/comment.info index 545726f1493982fdf78262cd24d62f7d701ce4dd..4127d72dd056b79d20c17494f7fcbe8b55bbd5c1 100644 --- a/modules/comment/comment.info +++ b/modules/comment/comment.info @@ -14,8 +14,8 @@ files[] = comment.tokens.inc configure = admin/content/comment stylesheets[all][] = comment.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 8f8e3a18b41ba4b0561e23a5d5b5ac8fa3021efa..2a0c073aff168a5cc3f8ab7f094f58d03b67106a 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -1,5 +1,5 @@ <?php -// $Id: comment.module,v 1.904 2010/10/05 06:17:28 webchick Exp $ +// $Id: comment.module,v 1.906 2010/10/20 01:31:06 dries Exp $ /** * @file @@ -1171,6 +1171,8 @@ function comment_form_node_form_alter(&$form, $form_state) { $comment_settings = ($node->comment == COMMENT_NODE_HIDDEN && empty($comment_count)) ? COMMENT_NODE_CLOSED : $node->comment; $form['comment_settings']['comment'] = array( '#type' => 'radios', + '#title' => t('Comments'), + '#title_display' => 'invisible', '#parents' => array('comment'), '#default_value' => $comment_settings, '#options' => array( @@ -1592,6 +1594,7 @@ function comment_delete_multiple($cids) { foreach ($comments as $comment) { field_attach_delete('comment', $comment); module_invoke_all('comment_delete', $comment); + module_invoke_all('entity_delete', $comment, 'comment'); // Delete the comment's replies. $child_cids = db_query('SELECT cid FROM {comment} WHERE pid = :cid', array(':cid' => $comment->cid))->fetchCol(); diff --git a/modules/contact/contact.info b/modules/contact/contact.info index 40b742091b352d15a200a59892ca925f63061b4f..e0afaa59b585408cbb56f220974f8bca10c29e62 100644 --- a/modules/contact/contact.info +++ b/modules/contact/contact.info @@ -11,8 +11,8 @@ files[] = contact.install files[] = contact.test configure = admin/structure/contact -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/contact/contact.pages.inc b/modules/contact/contact.pages.inc index 13720218777c835a714f88c8dc9242e4c8ee7fb4..0cf6c126f07fa2a244ae5cfdfc9df7711983d04c 100644 --- a/modules/contact/contact.pages.inc +++ b/modules/contact/contact.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: contact.pages.inc,v 1.43 2010/04/24 14:49:13 dries Exp $ +// $Id: contact.pages.inc,v 1.44 2010/10/08 03:36:03 webchick Exp $ /** * @file @@ -61,6 +61,7 @@ function contact_site_form($form, &$form_state) { $form['#attributes']['class'][] = 'user-info-from-cookie'; } + $form['#attributes']['class'][] = 'contact-form'; $form['name'] = array( '#type' => 'textfield', '#title' => t('Your name'), @@ -190,6 +191,7 @@ function contact_personal_form($form, &$form_state, $recipient) { $form['#attributes']['class'][] = 'user-info-from-cookie'; } + $form['#attributes']['class'][] = 'contact-form'; $form['recipient'] = array( '#type' => 'value', '#value' => $recipient, diff --git a/modules/contextual/contextual.info b/modules/contextual/contextual.info index 9ee3e44bddd19e31db5f2257684195fd975bd0b6..820fe382f4e37a217683af8f16886510bf3a3a0f 100644 --- a/modules/contextual/contextual.info +++ b/modules/contextual/contextual.info @@ -6,8 +6,8 @@ version = VERSION core = 7.x files[] = contextual.module -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/dashboard/dashboard.info b/modules/dashboard/dashboard.info index 343075f3faba6c6a6755a88490baa3eeb0f0412e..28952d18b7640080ab90ecff5396d04dd40b0289 100644 --- a/modules/dashboard/dashboard.info +++ b/modules/dashboard/dashboard.info @@ -9,8 +9,8 @@ files[] = dashboard.test dependencies[] = block configure = admin/dashboard/customize -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/dashboard/dashboard.module b/modules/dashboard/dashboard.module index b6350853f0dd1152ac918bad89607634f87a2226..376090a61e26c36459478efe69840746ff37c995 100644 --- a/modules/dashboard/dashboard.module +++ b/modules/dashboard/dashboard.module @@ -1,5 +1,5 @@ <?php -// $Id: dashboard.module,v 1.39 2010/10/03 00:14:57 dries Exp $ +// $Id: dashboard.module,v 1.40 2010/10/21 11:55:08 dries Exp $ /** * Implements hook_help(). @@ -19,7 +19,7 @@ function dashboard_help($path, $arg) { $output .= '</dl>'; return $output; - case 'admin/structure/dashboard': + case 'admin/dashboard/configure': // @todo This assumes the current page is being displayed using the same // theme that the dashboard is displayed in. $output = '<p>' . t('Rearrange blocks for display on the <a href="@dashboard-url">dashboard</a>. Disabling a block makes it available on the main <a href="@blocks-url">blocks administration page</a>.', array('@dashboard-url' => url('admin/dashboard'), '@blocks-url' => url("admin/structure/block/list/{$GLOBALS['theme_key']}"))) . '</p>'; @@ -31,12 +31,6 @@ function dashboard_help($path, $arg) { * Implements hook_menu(). */ function dashboard_menu() { - $items['admin/structure/dashboard'] = array( - 'title' => 'Dashboard', - 'description' => 'Configure which blocks can be shown on the dashboard.', - 'page callback' => 'dashboard_admin_blocks', - 'access arguments' => array('administer blocks'), - ); $items['admin/dashboard'] = array( 'title' => 'Dashboard', 'description' => 'View and customize your dashboard.', @@ -46,6 +40,13 @@ function dashboard_menu() { // the top corner of the window as a convenient "home link". 'weight' => -15, ); + $items['admin/dashboard/configure'] = array( + 'title' => 'Configure available dashboard blocks', + 'description' => 'Configure which blocks can be shown on the dashboard.', + 'page callback' => 'dashboard_admin_blocks', + 'access arguments' => array('administer blocks'), + 'type' => MENU_VISIBLE_IN_BREADCRUMB, + ); $items['admin/dashboard/customize'] = array( 'title' => 'Customize dashboard', 'description' => 'Customize your dashboard.', @@ -250,7 +251,7 @@ function dashboard_admin($launch_customize = FALSE) { ); $build = array( '#theme' => 'dashboard_admin', - '#message' => t('To customize the dashboard page, move blocks to the dashboard regions on the <a href="@dashboard">Dashboard administration page</a>, or enable JavaScript on this page to use the drag-and-drop interface.', array('@dashboard' => url('admin/structure/dashboard'))), + '#message' => t('To customize the dashboard page, move blocks to the dashboard regions on the <a href="@dashboard">Dashboard administration page</a>, or enable JavaScript on this page to use the drag-and-drop interface.', array('@dashboard' => url('admin/dashboard/configure'))), '#access' => user_access('administer blocks'), '#attached' => array( 'js' => array( @@ -277,7 +278,6 @@ function dashboard_admin($launch_customize = FALSE) { function dashboard_admin_blocks() { global $theme_key; drupal_theme_initialize(); - drupal_set_title(t('Configure available dashboard blocks')); module_load_include('inc', 'block', 'block.admin'); // Prepare the blocks for the current theme, and remove those that are @@ -330,10 +330,10 @@ function dashboard_form_dashboard_admin_display_form_alter(&$form, &$form_state) // dashboard blocks administration page. foreach ($form['blocks'] as &$block) { if (isset($block['configure']['#href'])) { - $block['configure']['#options']['query']['destination'] = 'admin/structure/dashboard'; + $block['configure']['#options']['query']['destination'] = 'admin/dashboard/configure'; } if (isset($block['delete']['#href'])) { - $block['delete']['#options']['query']['destination'] = 'admin/structure/dashboard'; + $block['delete']['#options']['query']['destination'] = 'admin/dashboard/configure'; } } } @@ -598,7 +598,7 @@ function theme_dashboard_region($variables) { */ function theme_dashboard_disabled_blocks($variables) { extract($variables); - $output = '<div class="canvas-content"><p>' . t('Drag and drop these blocks to the columns below. Changes are automatically saved. More options are available on the <a href="@dashboard-url">configuration page</a>.', array('@dashboard-url' => url('admin/structure/dashboard'))) . '</p>'; + $output = '<div class="canvas-content"><p>' . t('Drag and drop these blocks to the columns below. Changes are automatically saved. More options are available on the <a href="@dashboard-url">configuration page</a>.', array('@dashboard-url' => url('admin/dashboard/configure'))) . '</p>'; $output .= '<div id="disabled-blocks"><div class="region disabled-blocks clearfix">'; foreach ($blocks as $block) { $output .= theme('dashboard_disabled_block', array('block' => $block)); diff --git a/modules/dashboard/dashboard.test b/modules/dashboard/dashboard.test index ced6bce4461ff6eccd128a8df1150b595594d281..af96de79963487733894e2f688d7a5b07e72e6f4 100644 --- a/modules/dashboard/dashboard.test +++ b/modules/dashboard/dashboard.test @@ -1,5 +1,5 @@ <?php -// $Id: dashboard.test,v 1.5 2010/09/14 21:51:01 webchick Exp $ +// $Id: dashboard.test,v 1.6 2010/10/21 11:55:09 dries Exp $ /** * @file @@ -65,7 +65,7 @@ class DashboardBlocksTestCase extends DrupalWebTestCase { $dashboard_regions = dashboard_region_descriptions(); // Ensure blocks can be placed in dashboard regions. - $this->drupalGet('admin/structure/dashboard'); + $this->drupalGet('admin/dashboard/configure'); foreach ($dashboard_regions as $region => $description) { $elements = $this->xpath('//option[@value=:region]', array(':region' => $region)); $this->assertTrue(!empty($elements), t('%region is an available choice on the dashboard block configuration page.', array('%region' => $region))); diff --git a/modules/dblog/dblog.info b/modules/dblog/dblog.info index 2acabdc4a1e643a7a56dabe4b32bc3a0392cfb98..9b052c7f9de640f49f8a78229b838f0e5f1d2e2f 100644 --- a/modules/dblog/dblog.info +++ b/modules/dblog/dblog.info @@ -9,8 +9,8 @@ files[] = dblog.admin.inc files[] = dblog.install files[] = dblog.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc index 7c73aee782449586b04ee77323feb7419dd475f6..3727382bcb03660a811ca59864dc414224716312 100644 --- a/modules/field/field.form.inc +++ b/modules/field/field.form.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.form.inc,v 1.50 2010/06/17 13:44:45 dries Exp $ +// $Id: field.form.inc,v 1.51 2010/10/20 01:31:06 dries Exp $ /** * @file @@ -172,6 +172,8 @@ function field_multiple_value_form($field, $instance, $langcode, $items, &$form, // defined by widget. $element['_weight'] = array( '#type' => 'weight', + '#title' => t('Weight for row @number', array('@number' => $delta + 1)), + '#title_display' => 'invisible', // Note: this 'delta' is the FAPI 'weight' element's property. '#delta' => $max, '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta, diff --git a/modules/field/field.info b/modules/field/field.info index a5b76addf8b01425b822aa661cde9d903e1f37ea..95a5116bb0f832bfb71ac14196f3ba314e0399ca 100644 --- a/modules/field/field.info +++ b/modules/field/field.info @@ -17,8 +17,8 @@ dependencies[] = field_sql_storage required = TRUE stylesheets[all][] = theme/field.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index a484eecdafedd14e86b9c364b5f8aea785899958..faef36cbd5ca2bf1eb960fbc74c72e238566d4e2 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.info.inc,v 1.53 2010/09/11 00:03:42 webchick Exp $ +// $Id: field.info.inc,v 1.55 2010/10/23 01:34:02 webchick Exp $ /** * @file diff --git a/modules/field/field.install b/modules/field/field.install index 4a54c7b6190c1c81f657e83cac565e14f7057853..11fe78d325965bdf57cdfde233d627eb19dd40b5 100644 --- a/modules/field/field.install +++ b/modules/field/field.install @@ -1,5 +1,5 @@ <?php -// $Id: field.install,v 1.22 2010/09/29 19:46:40 dries Exp $ +// $Id: field.install,v 1.23 2010/10/20 01:15:58 dries Exp $ /** * @file @@ -309,11 +309,21 @@ function _update_7000_field_delete_instance($field_name, $entity_type, $bundle) /** * Utility function: fetch all the field definitions from the database. + * + * @param $conditions + * An array of conditions to limit the select query to. */ -function _update_7000_field_read_fields() { +function _update_7000_field_read_fields(array $conditions = array()) { $fields = array(); - $results = db_query('SELECT * FROM {field_config} WHERE deleted = 0', array(), array('fetch' => PDO::FETCH_ASSOC)); - foreach ($results as $record) { + $query = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC)) + ->fields('fc') + ->condition('deleted', 0); + if (!empty($conditions)) { + foreach ($conditions as $column => $value) { + $query->condition($column, $value); + } + } + foreach ($query->execute() as $record) { $field = unserialize($record['data']); $field['id'] = $record['id']; $field['field_name'] = $record['field_name']; diff --git a/modules/field/field.module b/modules/field/field.module index c623be9dd42b8071d56de5f94a1de107528208b3..074cc212bae8c7d75186b267548ef9077b48dbbd 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -1,5 +1,5 @@ <?php -// $Id: field.module,v 1.84 2010/09/05 02:21:38 dries Exp $ +// $Id: field.module,v 1.88 2010/10/23 01:34:02 webchick Exp $ /** * @file * Attach custom data fields to Drupal entities. @@ -854,6 +854,7 @@ function template_preprocess_field(&$variables, $hook) { // Add specific suggestions that can override the default implementation. $variables['theme_hook_suggestions'] = array( + 'field__' . $element['#field_type'], 'field__' . $element['#field_name'], 'field__' . $element['#bundle'], 'field__' . $element['#field_name'] . '__' . $element['#bundle'], @@ -965,3 +966,33 @@ function theme_field($variables) { return $output; } + +/** + * Helper form element validator: integer. + */ +function _element_validate_integer($element, &$form_state) { + $value = $element['#value']; + if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) { + form_error($element, t('%name must be an integer.', array('%name' => $element['#title']))); + } +} + +/** + * Helper form element validator: integer > 0. + */ +function _element_validate_integer_positive($element, &$form_state) { + $value = $element['#value']; + if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) { + form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title']))); + } +} + +/** + * Helper form element validator: number. + */ +function _element_validate_number($element, &$form_state) { + $value = $element['#value']; + if ($value != '' && !is_numeric($value)) { + form_error($element, t('%name must be a number.', array('%name' => $element['#title']))); + } +} diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc index 91e31cab94a3c2f50ec80b89f85e5c3fa528c444..fe227a784d4bc01da80ac6f047faf5879c372535 100644 --- a/modules/field/field.multilingual.inc +++ b/modules/field/field.multilingual.inc @@ -1,5 +1,5 @@ <?php -// $Id: field.multilingual.inc,v 1.13 2010/08/31 15:02:39 webchick Exp $ +// $Id: field.multilingual.inc,v 1.14 2010/10/09 03:43:31 webchick Exp $ /** * @file @@ -153,8 +153,7 @@ function _field_language_suggestion($available_languages, $language_suggestion, * An array of language codes. */ function field_content_languages() { - $languages = language_list('enabled'); - return array_keys($languages[1] + array(LANGUAGE_NONE => NULL)); + return array_keys(language_list() + array(LANGUAGE_NONE => NULL)); } /** 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 b93c651a3808e0e13e53bb878442ae8f750bbda1..a9936aa8d2fff528fc51ce2d4b34b27106582c01 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.info +++ b/modules/field/modules/field_sql_storage/field_sql_storage.info @@ -10,8 +10,8 @@ files[] = field_sql_storage.install files[] = field_sql_storage.test required = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/modules/list/list.info b/modules/field/modules/list/list.info index 23d0904227800394cb6c372f459d62ef09859a09..1cf247d15a4e87fbf7c16f00f099f1b0e38602d6 100644 --- a/modules/field/modules/list/list.info +++ b/modules/field/modules/list/list.info @@ -9,8 +9,8 @@ files[] = list.module files[] = tests/list.test required = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/modules/list/list.module b/modules/field/modules/list/list.module index 618583e26fc51b272cb596e0ee6c2804e7bc1b3f..4310ab4e0f0a4c24cc4c986a3adb89aee2c6575f 100644 --- a/modules/field/modules/list/list.module +++ b/modules/field/modules/list/list.module @@ -1,5 +1,5 @@ <?php -// $Id: list.module,v 1.34 2010/09/04 15:40:51 dries Exp $ +// $Id: list.module,v 1.35 2010/10/11 19:57:00 dries Exp $ /** * @file @@ -84,9 +84,8 @@ function list_field_settings_form($field, $instance, $has_data) { $off_value = array_shift($values); $on_value = array_shift($values); $form['allowed_values'] = array( - '#type' => 'markup', + '#type' => 'value', '#description' => '', - '#input' => TRUE, '#value_callback' => 'list_boolean_allowed_values_callback', '#access' => empty($settings['allowed_values_function']), ); @@ -96,6 +95,9 @@ function list_field_settings_form($field, $instance, $has_data) { '#default_value' => $on_value, '#required' => FALSE, '#description' => t('If left empty, "1" will be used.'), + // Change #parents to make sure the element is not saved into field + // settings. + '#parents' => array('on'), ); $form['allowed_values']['off'] = array( '#type' => 'textfield', @@ -103,7 +105,14 @@ function list_field_settings_form($field, $instance, $has_data) { '#default_value' => $off_value, '#required' => FALSE, '#description' => t('If left empty, "0" will be used.'), + // Change #parents to make sure the element is not saved into field + // settings. + '#parents' => array('off'), ); + // Link the allowed value to the on / off elements to prepare for the rare + // case of an alter changing #parents. + $form['allowed_values']['#on_parents'] = &$form['allowed_values']['on']['#parents']; + $form['allowed_values']['#off_parents'] = &$form['allowed_values']['off']['#parents']; } // Alter the description for allowed values depending on the widget type. @@ -156,13 +165,10 @@ function list_allowed_values_setting_validate($element, &$form_state) { /** * Form element #value_callback: assembles the allowed values for 'boolean' fields. */ -function list_boolean_allowed_values_callback($element, $edit = FALSE) { - if ($edit !== FALSE) { - $on = $edit['on']; - $off = $edit['off']; - $edit = "0|$off\n1|$on"; - return $edit; - } +function list_boolean_allowed_values_callback($element, $input, $form_state) { + $on = drupal_array_get_nested_value($form_state['input'], $element['#on_parents']); + $off = drupal_array_get_nested_value($form_state['input'], $element['#off_parents']); + return "0|$off\n1|$on"; } /** diff --git a/modules/field/modules/list/tests/list.test b/modules/field/modules/list/tests/list.test index 56b2595e565bbf1e37e224110cc11d1603f5638b..3a1472dad27b1e4ebbb62f48a6f099f3fff2b4a2 100644 --- a/modules/field/modules/list/tests/list.test +++ b/modules/field/modules/list/tests/list.test @@ -1,5 +1,5 @@ <?php -// $Id: list.test,v 1.7 2010/08/05 23:53:37 webchick Exp $ +// $Id: list.test,v 1.8 2010/10/11 19:57:00 dries Exp $ /** * @file @@ -134,26 +134,46 @@ class ListFieldUITestCase extends FieldTestCase { function testAllowedValues() { $element_name = "field[settings][allowed_values]"; - //Test 'List' field type. + // Test 'List' field type. $admin_path = $this->createListFieldAndEdit('list'); - //Check that non-integer keys are rejected. + // Check that non-integer keys are rejected. $edit = array($element_name => "1.1|one\n"); $this->drupalPost($admin_path, $edit, t('Save settings')); $this->assertText("keys must be integers", t('Form validation failed.')); // Test 'List (number)' field type. $admin_path = $this->createListFieldAndEdit('list_number'); - //Check that non-numeric keys are rejected. + // Check that non-numeric keys are rejected. $edit = array($element_name => "1|one\nB|two"); $this->drupalPost($admin_path, $edit, t('Save settings')); $this->assertText("each key must be a valid integer or decimal", t('Form validation failed.')); - //Test 'List (text)' field type. + // Test 'List (text)' field type. $admin_path = $this->createListFieldAndEdit('list_text'); - //Check that over long keys are rejected. + // Check that overly long keys are rejected. $edit = array($element_name => "1|one\n" . $this->randomName(256) . "|two"); $this->drupalPost($admin_path, $edit, t('Save settings')); $this->assertText("each key must be a string at most 255 characters long", t('Form validation failed.')); + + // Test 'Boolean' field type. + $admin_path = $this->createListFieldAndEdit('list_boolean'); + // Check that the seperate 'On' and 'Off' form fields work. + $on = $this->randomName(); + $off = $this->randomName(); + $edit = array( + 'on' => $on, + 'off' => $off, + ); + $this->drupalPost($admin_path, $edit, t('Save settings')); + $this->assertText("Saved test_list_boolean configuration.", t("The 'On' and 'Off' form fields work for boolean fields.")); + // Test the allowed_values on the field settings form. + $this->drupalGet($admin_path); + $this->assertFieldByName('on', $on, t("The 'On' value is stored correctly.")); + $this->assertFieldByName('off', $off, t("The 'Off' value is stored correctly.")); + $field = field_info_field($this->field['field_name']); + $this->assertEqual($field['settings']['allowed_values'], "0|$off\n1|$on", t('The allowed value is correct')); + $this->assertFalse(isset($field['settings']['on']), t('The on value is not saved into settings')); + $this->assertFalse(isset($field['settings']['off']), t('The off value is not saved into settings')); } /** @@ -162,7 +182,7 @@ class ListFieldUITestCase extends FieldTestCase { * @param string $type * 'list', 'list_boolean', 'list_number', or 'list_text' */ - private function createListFieldAndEdit($type) { + protected function createListFieldAndEdit($type) { // Create a test field and instance. $field_name = 'test_' . $type; $field = array( @@ -170,6 +190,7 @@ class ListFieldUITestCase extends FieldTestCase { 'type' => $type, ); field_create_field($field); + $this->field = $field; $instance = array( 'field_name' => $field_name, 'entity_type' => 'node', @@ -182,4 +203,3 @@ class ListFieldUITestCase extends FieldTestCase { } } - diff --git a/modules/field/modules/list/tests/list_test.info b/modules/field/modules/list/tests/list_test.info index 2e356d89221b63995d2d372af9c10ec6f30e09f2..c045041943c69d87e27d76bf206abb8383033159 100644 --- a/modules/field/modules/list/tests/list_test.info +++ b/modules/field/modules/list/tests/list_test.info @@ -7,8 +7,8 @@ files[] = list_test.module version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/modules/number/number.info b/modules/field/modules/number/number.info index 4a90975d5d541ffb9c7db44bb272e3f93e186f8a..f6c4bf3a11c4a7ae9a0581b9ea7f24d95809e9db 100644 --- a/modules/field/modules/number/number.info +++ b/modules/field/modules/number/number.info @@ -8,8 +8,8 @@ dependencies[] = field files[] = number.module required = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/modules/number/number.module b/modules/field/modules/number/number.module index 61d0f9dcfe3ba4fe9d53764c6d858e56f16bef11..4d0f7b8f3a52867a686d892ff9ed95f77cde4937 100644 --- a/modules/field/modules/number/number.module +++ b/modules/field/modules/number/number.module @@ -1,5 +1,5 @@ <?php -// $Id: number.module,v 1.43 2010/09/07 17:56:41 webchick Exp $ +// $Id: number.module,v 1.44 2010/10/22 00:42:42 dries Exp $ /** * @file @@ -222,7 +222,7 @@ function number_field_formatter_settings_form($field, $instance, $view_mode, $fo ',' => t('Comma'), ' ' => t('Space'), ); - $form['thousand_separator'] = array( + $element['thousand_separator'] = array( '#type' => 'select', '#title' => t('Thousand marker'), '#options' => $options, @@ -230,13 +230,13 @@ function number_field_formatter_settings_form($field, $instance, $view_mode, $fo ); if ($display['type'] == 'number_decimal' || $display['type'] == 'number_float') { - $form['decimal_separator'] = array( + $element['decimal_separator'] = array( '#type' => 'select', '#title' => t('Decimal marker'), '#options' => array('.' => t('Decimal point'), ',' => t('Comma')), '#default_value' => $settings['decimal_separator'], ); - $form['scale'] = array( + $element['scale'] = array( '#type' => 'select', '#title' => t('Scale'), '#options' => drupal_map_assoc(range(0, 10)), @@ -245,13 +245,13 @@ function number_field_formatter_settings_form($field, $instance, $view_mode, $fo ); } - $form['prefix_suffix'] = array( + $element['prefix_suffix'] = array( '#type' => 'checkbox', '#title' => t('Display prefix and suffix.'), '#default_value' => $settings['prefix_suffix'], ); - return $form; + return $element; } /** diff --git a/modules/field/modules/options/options.info b/modules/field/modules/options/options.info index 596dff99abeb73b2dbd1cb8e63524ca8b5d4125d..0ef6e0415aeb4124cd1e20286f96f39f341dcbb3 100644 --- a/modules/field/modules/options/options.info +++ b/modules/field/modules/options/options.info @@ -9,8 +9,8 @@ files[] = options.module files[] = options.test required = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/modules/text/text.info b/modules/field/modules/text/text.info index 15ce9c09f542d88ac02f2fab180f4d9cac0fa9ee..a2ed62fd0605434a2483d2ff6167b906aaa1e0f0 100644 --- a/modules/field/modules/text/text.info +++ b/modules/field/modules/text/text.info @@ -9,8 +9,8 @@ files[] = text.module files[] = text.test required = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/modules/text/text.install b/modules/field/modules/text/text.install index ce0178ceaa7440b9d4f663a687f09803ca28d084..cbb012c4f264bd32f581c076092067cac26e2c1b 100644 --- a/modules/field/modules/text/text.install +++ b/modules/field/modules/text/text.install @@ -1,5 +1,5 @@ <?php -// $Id: text.install,v 1.2 2010/09/29 01:37:02 dries Exp $ +// $Id: text.install,v 1.4 2010/10/20 15:57:42 webchick Exp $ /** * @file @@ -48,8 +48,8 @@ function text_field_schema($field) { } $columns += array( 'format' => array( - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 255, 'not null' => FALSE, ), ); @@ -66,3 +66,44 @@ function text_field_schema($field) { ), ); } + +/** + * Implements hook_update_dependencies(). + */ +function text_update_dependencies() { + // Ensure that format columns are only changed after Filter module has changed + // the primary records. + $dependencies['text'][7000] = array( + 'filter' => 7010, + ); + + return $dependencies; +} + +/** + * Change text field 'format' columns into varchar. + */ +function text_update_7000() { + $spec = array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ); + $fields = _update_7000_field_read_fields(array( + 'module' => 'text', + 'storage_type' => 'field_sql_storage', + )); + foreach ($fields as $field_name => $field) { + if ($field['deleted']) { + $table = "field_deleted_data_{$field['id']}"; + $revision_table = "field_deleted_revision_{$field['id']}"; + } + else { + $table = "field_data_{$field['field_name']}"; + $revision_table = "field_revision_{$field['field_name']}"; + } + $column = $field['field_name'] . '_' . 'format'; + db_change_field($table, $column, $column, $spec); + db_change_field($revision_table, $column, $column, $spec); + } +} diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index 5dae9c8b7dffb3b62300a97baac0ff9ded761550..d0c5d8da220a016fe5048360998267f5e7792ed2 100644 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -1,5 +1,5 @@ <?php -// $Id: text.module,v 1.65 2010/09/18 02:18:35 dries Exp $ +// $Id: text.module,v 1.66 2010/10/22 00:42:42 dries Exp $ /** * @file @@ -220,10 +220,10 @@ function text_field_formatter_settings_form($field, $instance, $view_mode, $form $display = $instance['display'][$view_mode]; $settings = $display['settings']; - $form = array(); + $element = array(); if (strpos($display['type'], '_trimmed') !== FALSE) { - $form['trim_length'] = array( + $element['trim_length'] = array( '#title' => t('Trim length'), '#type' => 'textfield', '#size' => 10, @@ -233,7 +233,7 @@ function text_field_formatter_settings_form($field, $instance, $view_mode, $form ); } - return $form; + return $element; } /** diff --git a/modules/field/modules/text/text.test b/modules/field/modules/text/text.test index eaab367c71b4796814747b0f8be4d04e469c34ce..35df54349114d471ca74a5a9464e5dbbdace4200 100644 --- a/modules/field/modules/text/text.test +++ b/modules/field/modules/text/text.test @@ -1,5 +1,5 @@ <?php -// $Id: text.test,v 1.29 2010/09/24 00:37:42 dries Exp $ +// $Id: text.test,v 1.30 2010/10/20 01:15:58 dries Exp $ class TextFieldTestCase extends DrupalWebTestCase { protected $instance; @@ -198,12 +198,16 @@ class TextFieldTestCase extends DrupalWebTestCase { // Create a new text format that does not escape HTML, and grant the user // access to it. $this->drupalLogin($this->admin_user); - $edit = array('name' => $this->randomName()); + $edit = array( + 'format' => drupal_strtolower($this->randomName()), + 'name' => $this->randomName(), + ); $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); filter_formats_reset(); $this->checkPermissions(array(), TRUE); - $format_id = db_query("SELECT format FROM {filter_format} WHERE name = :name", array(':name' => $edit['name']))->fetchField(); - $permission = filter_permission_name(filter_format_load($format_id)); + $format = filter_format_load($edit['format']); + $format_id = $format->format; + $permission = filter_permission_name($format); $rid = max(array_keys($this->web_user->roles)); user_role_grant_permissions($rid, array($permission)); $this->drupalLogin($this->web_user); @@ -360,8 +364,8 @@ class TextSummaryTestCase extends DrupalWebTestCase { // Test text_summary() for different sizes. for ($i = 0; $i <= 37; $i++) { $this->callTextSummary($text, $expected[$i], NULL, $i); - $this->callTextSummary($text, $expected_lb[$i], 1, $i); - $this->callTextSummary($text, $expected_lb[$i], 2, $i); + $this->callTextSummary($text, $expected_lb[$i], 'plain_text', $i); + $this->callTextSummary($text, $expected_lb[$i], 'filtered_html', $i); } } @@ -386,8 +390,15 @@ class TextTranslationTestCase extends DrupalWebTestCase { function setUp() { parent::setUp('locale', 'translation'); - $this->format = 3; - $this->admin = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages', 'bypass node access', "use text format $this->format")); + $full_html_format = filter_format_load('full_html'); + $this->format = $full_html_format->format; + $this->admin = $this->drupalCreateUser(array( + 'administer languages', + 'administer content types', + 'access administration pages', + 'bypass node access', + filter_permission_name($full_html_format), + )); $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content')); // Enable an additional language. @@ -456,11 +467,11 @@ class TextTranslationTestCase extends DrupalWebTestCase { // Populate the body field: the first item gets the "Full HTML" input // format, the second one "Filtered HTML". - $format = $this->format; + $formats = array('full_html', 'filtered_html'); foreach ($body as $delta => $value) { $edit = array( "body[$langcode][$delta][value]" => $value, - "body[$langcode][$delta][format]" => $format--, + "body[$langcode][$delta][format]" => array_shift($formats), ); $this->drupalPost('node/1/edit', $edit, t('Save')); $this->assertText($body[$delta], t('The body field with delta @delta has been saved.', array('@delta' => $delta))); diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index b75882fa768ad6f0b4c71bad66ed5fba25ca9ad5..46712920eff58a541921f0415d1c77b61e9460ba 100644 --- a/modules/field/tests/field_test.field.inc +++ b/modules/field/tests/field_test.field.inc @@ -1,5 +1,5 @@ <?php -// $Id: field_test.field.inc,v 1.13 2010/09/28 02:30:31 dries Exp $ +// $Id: field_test.field.inc,v 1.14 2010/10/20 00:13:33 dries Exp $ /** * @file @@ -257,6 +257,61 @@ function field_test_field_formatter_info() { ); } +/** + * Implements hook_field_formatter_settings_form(). + */ +function field_test_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $element = array(); + + // The name of the setting depends on the formatter type. + $map = array( + 'field_test_default' => 'test_formatter_setting', + 'field_test_multiple' => 'test_formatter_setting_multiple', + 'field_test_with_prepare_view' => 'test_formatter_setting_additional', + ); + + if (isset($map[$display['type']])) { + $name = $map[$display['type']]; + + $element[$name] = array( + '#title' => t('Setting'), + '#type' => 'textfield', + '#size' => 20, + '#default_value' => $settings[$name], + '#required' => TRUE, + ); + } + + return $element; +} + +/** + * Implements hook_field_formatter_settings_summary(). + */ +function field_test_field_formatter_settings_summary($field, $instance, $view_mode) { + $display = $instance['display'][$view_mode]; + $settings = $display['settings']; + + $summary = ''; + + // The name of the setting depends on the formatter type. + $map = array( + 'field_test_default' => 'test_formatter_setting', + 'field_test_multiple' => 'test_formatter_setting_multiple', + 'field_test_with_prepare_view' => 'test_formatter_setting_additional', + ); + + if (isset($map[$display['type']])) { + $name = $map[$display['type']]; + $summary = t('@setting: @value', array('@setting' => $name, '@value' => $settings[$name])); + } + + return $summary; +} + /** * Implements hook_field_formatter_prepare_view(). */ diff --git a/modules/field/tests/field_test.info b/modules/field/tests/field_test.info index 9baafffc0d5733ee222a27177a7ee417eb2cfeb0..21adeb274178fe7e667504a79d92127ab51e7df7 100644 --- a/modules/field/tests/field_test.info +++ b/modules/field/tests/field_test.info @@ -11,8 +11,8 @@ files[] = field_test.install version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field/tests/field_test.storage.inc b/modules/field/tests/field_test.storage.inc index 3a7a59a13111cade65c9b4252715f61fae595246..84b75a4a2b5bdc75bba5a443cefc28f31fa7ac74 100644 --- a/modules/field/tests/field_test.storage.inc +++ b/modules/field/tests/field_test.storage.inc @@ -1,5 +1,5 @@ <?php -// $Id: field_test.storage.inc,v 1.7 2010/09/26 23:31:35 dries Exp $ +// $Id: field_test.storage.inc,v 1.8 2010/10/13 05:19:26 webchick Exp $ /** * @file @@ -453,10 +453,9 @@ function field_test_field_attach_rename_bundle($bundle_old, $bundle_new) { /** * Implements hook_field_attach_delete_bundle(). */ -function field_test_field_attach_delete_bundle($bundle, $instances) { +function field_test_field_attach_delete_bundle($entity_type, $bundle, $instances) { $data = _field_test_storage_data(); - $instances = field_info_instances($bundle); foreach ($instances as $field_name => $instance) { $field = field_info_field($field_name); if ($field['storage']['type'] == 'field_test_storage') { diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc index fb336d5627428906dd64b4ad2cba8db12c2f8b67..25fe69285df6018ad065336e6c6137053369efe6 100644 --- a/modules/field_ui/field_ui.admin.inc +++ b/modules/field_ui/field_ui.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: field_ui.admin.inc,v 1.71 2010/10/04 18:00:45 dries Exp $ +// $Id: field_ui.admin.inc,v 1.79 2010/10/23 01:34:02 webchick Exp $ /** * @file @@ -175,12 +175,10 @@ function field_ui_table_pre_render($elements) { $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], '_field_ui_reduce_order'); } - drupal_add_js(array('fieldUIRowsData' => $js_settings), 'setting'); - // @todo : use #attached instead when http://drupal.org/node/561858 is fixed. -// $elements['#attached']['js'][] = array( -// 'type' => 'setting', -// 'data' => array('fieldRowsData' => $js_settings), -// ); + $elements['#attached']['js'][] = array( + 'type' => 'setting', + 'data' => array('fieldUIRowsData' => $js_settings), + ); return $elements; } @@ -328,6 +326,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'weight' => array( '#type' => 'textfield', + '#title' => t('Weight for @title', array('@title' => $instance['label'])), + '#title_display' => 'invisible', '#default_value' => $instance['widget']['weight'], '#size' => 3, '#attributes' => array('class' => array('field-weight')), @@ -335,7 +335,10 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', + '#title' => t('Parent for @title', array('@title' => $instance['label'])), + '#title_display' => 'invisible', '#options' => $table['#parent_options'], + '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $name, 'parent'), ), @@ -396,12 +399,15 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle '#size' => 3, '#attributes' => array('class' => array('field-weight')), '#title_display' => 'invisible', - '#title' => t('Weight for @row', array('@row' => $extra_field['label'])), + '#title' => t('Weight for @title', array('@title' => $extra_field['label'])), ), 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', + '#title' => t('Parent for @title', array('@title' => $extra_field['label'])), + '#title_display' => 'invisible', '#options' => $table['#parent_options'], + '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $name, 'parent'), ), @@ -439,6 +445,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle '#region_callback' => 'field_ui_field_overview_row_region', 'label' => array( '#type' => 'textfield', + '#title' => t('New field label'), + '#title_display' => 'invisible', '#size' => 15, '#description' => t('Label'), '#prefix' => '<div class="label-input"><div class="add-new-placeholder">' . t('Add new field') .'</div>', @@ -456,7 +464,10 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', + '#title' => t('Parent for new field'), + '#title_display' => 'invisible', '#options' => $table['#parent_options'], + '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#prefix' => '<div class="add-new-placeholder"> </div>', '#parents' => array('fields', $name, 'parent'), @@ -469,6 +480,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'field_name' => array( '#type' => 'textfield', + '#title' => t('New field name'), + '#title_display' => 'invisible', // This field should stay LTR even for RTL languages. '#field_prefix' => '<span dir="ltr">field_', '#field_suffix' => '</span>‎', @@ -479,6 +492,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'type' => array( '#type' => 'select', + '#title' => t('Type of new field'), + '#title_display' => 'invisible', '#options' => $field_type_options, '#empty_option' => t('- Select a field type -'), '#description' => t('Type of data to store.'), @@ -487,6 +502,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'widget_type' => array( '#type' => 'select', + '#title' => t('Widget for new field'), + '#title_display' => 'invisible', '#options' => $widget_type_options, '#empty_option' => t('- Select a widget -'), '#description' => t('Form element to edit the data.'), @@ -507,6 +524,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle '#region_callback' => 'field_ui_field_overview_row_region', 'label' => array( '#type' => 'textfield', + '#title' => t('Existing field label'), + '#title_display' => 'invisible', '#size' => 15, '#description' => t('Label'), '#attributes' => array('class' => array('label-textfield')), @@ -525,7 +544,10 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', + '#title' => t('Parent for existing field'), + '#title_display' => 'invisible', '#options' => $table['#parent_options'], + '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#prefix' => '<div class="add-new-placeholder"> </div>', '#parents' => array('fields', $name, 'parent'), @@ -538,6 +560,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'field_name' => array( '#type' => 'select', + '#title' => t('Existing field to share'), + '#title_display' => 'invisible', '#options' => $existing_field_options, '#empty_option' => t('- Select an existing field -'), '#description' => t('Field to share'), @@ -547,6 +571,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle ), 'widget_type' => array( '#type' => 'select', + '#title' => t('Widget for existing field'), + '#title_display' => 'invisible', '#options' => $widget_type_options, '#empty_option' => t('- Select a widget -'), '#description' => t('Form element to edit the data.'), @@ -882,6 +908,8 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund ), 'weight' => array( '#type' => 'textfield', + '#title' => t('Weight for @title', array('@title' => $instance['label'])), + '#title_display' => 'invisible', '#default_value' => $display['weight'], '#size' => 3, '#attributes' => array('class' => array('field-weight')), @@ -889,7 +917,10 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', + '#title' => t('Label display for @title', array('@title' => $instance['label'])), + '#title_display' => 'invisible', '#options' => $table['#parent_options'], + '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $name, 'parent'), ), @@ -901,6 +932,8 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund ), 'label' => array( '#type' => 'select', + '#title' => t('Label display for @title', array('@title' => $instance['label'])), + '#title_display' => 'invisible', '#options' => $field_label_options, '#default_value' => $display['label'], ), @@ -911,6 +944,8 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund $table[$name]['format'] = array( 'type' => array( '#type' => 'select', + '#title' => t('Formatter for @title', array('@title' => $instance['label'])), + '#title_display' => 'invisible', '#options' => $formatter_options, '#default_value' => $display['type'], '#parents' => array('fields', $name, 'type'), @@ -956,7 +991,7 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund if ($form_state['formatter_settings_edit'] == $name) { // We are currently editing this field's formatter settings. Display the // settings form and submit buttons. - $table[$name]['settings_edit_form'] = array(); + $table[$name]['format']['settings_edit_form'] = array(); $settings_form = array(); $function = $formatter['module'] . '_field_formatter_settings_form'; @@ -1035,6 +1070,8 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund ), 'weight' => array( '#type' => 'textfield', + '#title' => t('Weight for @title', array('@title' => $extra_field['label'])), + '#title_display' => 'invisible', '#default_value' => $display['weight'], '#size' => 3, '#attributes' => array('class' => array('field-weight')), @@ -1042,7 +1079,10 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund 'parent_wrapper' => array( 'parent' => array( '#type' => 'select', + '#title' => t('Parents for @title', array('@title' => $extra_field['label'])), + '#title_display' => 'invisible', '#options' => $table['#parent_options'], + '#empty_value' => '', '#attributes' => array('class' => array('field-parent')), '#parents' => array('fields', $name, 'parent'), ), @@ -1058,6 +1098,8 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund 'format' => array( 'type' => array( '#type' => 'select', + '#title' => t('Visibility for @title', array('@title' => $extra_field['label'])), + '#title_display' => 'invisible', '#options' => $extra_visibility_options, '#default_value' => $display['visible'] ? 'visible' : 'hidden', '#parents' => array('fields', $name, 'type'), @@ -1111,9 +1153,6 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund '#type' => 'submit', '#value' => t('Refresh'), '#op' => 'refresh_table', - // Do not check errors, but make sure we get the values of the - // 'refresh_rows' input. - '#limit_validation_errors' => array(array('refresh_rows')), '#submit' => array('field_ui_display_overview_multistep_submit'), '#ajax' => array( 'callback' => 'field_ui_display_overview_multistep_js', @@ -1132,11 +1171,8 @@ function field_ui_display_overview_form($form, &$form_state, $entity_type, $bund $form['#attached']['css'][] = drupal_get_path('module', 'field_ui') . '/field_ui.css'; // Add tabledrag behavior. - drupal_add_tabledrag('field-display-overview', 'order', 'sibling', 'field-weight'); - drupal_add_tabledrag('field-display-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name'); -// @todo : use #attached instead when http://drupal.org/node/561858 is fixed. -// $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'order', 'sibling', 'field-weight'); -// $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name'); + $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'order', 'sibling', 'field-weight'); + $form['#attached']['drupal_add_tabledrag'][] = array('field-display-overview', 'match', 'parent', 'field-parent', 'field-parent', 'field-name'); return $form; } @@ -1438,7 +1474,7 @@ function field_ui_field_settings_form($form, &$form_state, $instance) { // If so, prevent changes to the field settings. $has_data = field_has_data($field); if ($has_data) { - $form['field']['#description'] = '<div class=error>' . t('There is data for this field in the database. The field settings can no longer be changed.' . '</div>') . $form['field']['#description']; + $form['field']['#description'] = '<div class="messages error">' . t('There is data for this field in the database. The field settings can no longer be changed.') . '</div>' . $form['field']['#description']; } // Build the non-configurable field values. @@ -1910,33 +1946,3 @@ function field_ui_next_destination($entity_type, $bundle) { $admin_path = _field_ui_bundle_admin_path($entity_type, $bundle); return $admin_path . '/fields'; } - -/** - * Helper form element validator: integer. - */ -function _element_validate_integer($element, &$form_state) { - $value = $element['#value']; - if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) { - form_error($element, t('%name must be an integer.', array('%name' => $element['#title']))); - } -} - -/** - * Helper form element validator: integer > 0. - */ -function _element_validate_integer_positive($element, &$form_state) { - $value = $element['#value']; - if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) { - form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title']))); - } -} - -/** - * Helper form element validator: number. - */ -function _element_validate_number($element, &$form_state) { - $value = $element['#value']; - if ($value != '' && !is_numeric($value)) { - form_error($element, t('%name must be a number.', array('%name' => $element['#title']))); - } -} diff --git a/modules/field_ui/field_ui.info b/modules/field_ui/field_ui.info index 43640fc05909aafa661bf7c8280c2d35a618f114..e054fe8bb9e6c4aaf48ace350badbc7953bd53b6 100644 --- a/modules/field_ui/field_ui.info +++ b/modules/field_ui/field_ui.info @@ -9,8 +9,8 @@ files[] = field_ui.module files[] = field_ui.admin.inc files[] = field_ui.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/field_ui/field_ui.test b/modules/field_ui/field_ui.test index 715fce4c7580742995cab35109ba9b3a914bb909..710a13b27548f20acdaedd9426943ab619b33afb 100644 --- a/modules/field_ui/field_ui.test +++ b/modules/field_ui/field_ui.test @@ -1,5 +1,5 @@ <?php -// $Id: field_ui.test,v 1.22 2010/09/11 00:03:42 webchick Exp $ +// $Id: field_ui.test,v 1.25 2010/10/23 01:34:02 webchick Exp $ /** * @file @@ -7,19 +7,13 @@ */ /** - * Field UI tests. + * Helper class for Field UI test classes. */ class FieldUITestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Field UI tests', - 'description' => 'Test the field UI functionality.', - 'group' => 'Field UI', - ); - } - function setUp() { - parent::setUp('field_test'); + function setUp($modules = array()) { + array_unshift($modules, 'field_test'); + parent::setUp($modules); // Create test user. $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy')); @@ -31,6 +25,119 @@ class FieldUITestCase extends DrupalWebTestCase { $this->type = $type->type; // Store a valid URL name, with hyphens instead of underscores. $this->hyphen_type = str_replace('_', '-', $this->type); + } + + /** + * Create a new field through the Field UI. + * + * @param $bundle_path + * Path of the 'Manage fields' page for the bundle. + * @param $initial_edit + * $edit parameter for drupalPost() on the first step ('Manage fields' + * screen). + * @param $field_edit + * $edit parameter for drupalPost() on the second step ('Field settings' + * form). + * @param $instance_edit + * $edit parameter for drupalPost() on the third step ('Instance settings' + * form). + */ + function fieldUIAddNewField($bundle_path, $initial_edit, $field_edit = array(), $instance_edit = array()) { + // Use 'test_field' field type by default. + $initial_edit += array( + 'fields[_add_new_field][type]' => 'test_field', + 'fields[_add_new_field][widget_type]' => 'test_field_widget', + ); + $label = $initial_edit['fields[_add_new_field][label]']; + $field_name = $initial_edit['fields[_add_new_field][field_name]']; + + // First step : 'Add new field' on the 'Manage fields' page. + $this->drupalPost("$bundle_path/fields", $initial_edit, t('Save')); + $this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), t('Field settings page was displayed.')); + + // Second step : 'Field settings' form. + $this->drupalPost(NULL, $field_edit, t('Save field settings')); + $this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), t('Redirected to instance and widget settings page.')); + + // Third step : 'Instance settings' form. + $this->drupalPost(NULL, $instance_edit, t('Save settings')); + $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.')); + + // Check that the field appears in the overview form. + $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.')); + } + + /** + * Add an existing field through the Field UI. + * + * @param $bundle_path + * Path of the 'Manage fields' page for the bundle. + * @param $initial_edit + * $edit parameter for drupalPost() on the first step ('Manage fields' + * screen). + * @param $instance_edit + * $edit parameter for drupalPost() on the second step ('Instance settings' + * form). + */ + function fieldUIAddExistingField($bundle_path, $initial_edit, $instance_edit = array()) { + // Use 'test_field_widget' by default. + $initial_edit += array( + 'fields[_add_existing_field][widget_type]' => 'test_field_widget', + ); + $label = $initial_edit['fields[_add_existing_field][label]']; + $field_name = $initial_edit['fields[_add_existing_field][field_name]']; + + // First step : 'Add existing field' on the 'Manage fields' page. + $this->drupalPost("$bundle_path/fields", $initial_edit, t('Save')); + + // Second step : 'Instance settings' form. + $this->drupalPost(NULL, $instance_edit, t('Save settings')); + $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.')); + + // Check that the field appears in the overview form. + $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.')); + } + + /** + * Delete a field instance through the Field UI. + * + * @param $bundle_path + * Path of the 'Manage fields' page for the bundle. + * @param $field_name + * The name of the field. + * @param $label + * The label of the field. + * @param $bundle_label + * The label of the bundle. + */ + function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_label) { + // Display confirmation form. + $this->drupalGet("$bundle_path/fields/$field_name/delete"); + $this->assertRaw(t('Are you sure you want to delete the field %label', array('%label' => $label)), t('Delete confirmation was found.')); + + // Submit confirmation form. + $this->drupalPost(NULL, array(), t('Delete')); + $this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%label' => $label, '%type' => $bundle_label)), t('Delete message was found.')); + + // Check that the field does not appear in the overview form. + $this->assertNoFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $label, t('Field does not appear in the overview page.')); + } +} + +/** + * Field UI tests for the 'Manage fields' screen. + */ +class FieldUIManageFieldsTestCase extends FieldUITestCase { + public static function getInfo() { + return array( + 'name' => 'Manage fields', + 'description' => 'Test the Field UI "Manage fields" screen.', + 'group' => 'Field UI', + ); + } + + function setUp() { + parent::setUp(); // Create random field name. $this->field_label = $this->randomName(8); @@ -298,103 +405,69 @@ class FieldUITestCase extends DrupalWebTestCase { $this->drupalGet($bundle_path); $this->assertFalse($this->xpath('//select[@id="edit-add-existing-field-field-name"]//option[@value=:field_name]', array(':field_name' => $field_name)), t("The 'add existing field' select respects field types 'no_ui' property.")); } +} - /** - * Create a new field through the Field UI. - * - * @param $bundle_path - * Path of the 'Manage fields' page for the bundle. - * @param $initial_edit - * $edit parameter for drupalPost() on the first step ('Manage fields' - * screen). - * @param $field_edit - * $edit parameter for drupalPost() on the first step ('Field settings' - * form). - * @param $instance_edit - * $edit parameter for drupalPost() on the second step ('Instance settings' - * form). - */ - function fieldUIAddNewField($bundle_path, $initial_edit, $field_edit = array(), $instance_edit = array()) { - // Use 'test_field' field type by default. - $initial_edit += array( - 'fields[_add_new_field][type]' => 'test_field', - 'fields[_add_new_field][widget_type]' => 'test_field_widget', +/** + * Field UI tests for the 'Manage display' screens. + */ +class FieldUIManageDisplayTestCase extends FieldUITestCase { + public static function getInfo() { + return array( + 'name' => 'Manage display', + 'description' => 'Test the Field UI "Manage display" screens.', + 'group' => 'Field UI', ); - $label = $initial_edit['fields[_add_new_field][label]']; - $field_name = $initial_edit['fields[_add_new_field][field_name]']; - - // First step : 'Add new field' on the 'Manage fields' page. - $this->drupalPost("$bundle_path/fields", $initial_edit, t('Save')); - $this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), t('Field settings page was displayed.')); - - // Second step : 'Field settings' form. - $this->drupalPost(NULL, $field_edit, t('Save field settings')); - $this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), t('Redirected to instance and widget settings page.')); - - // Assert the field settings are correct. - $this->assertFieldSettings($this->type, $this->field_name); - - // Third step : 'Instance settings' form. - $this->drupalPost(NULL, $instance_edit, t('Save settings')); - $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.')); - - // Check that the field appears in the overview form. - $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.')); } - /** - * Add an existing field through the Field UI. - * - * @param $bundle_path - * Path of the 'Manage fields' page for the bundle. - * @param $initial_edit - * $edit parameter for drupalPost() on the first step ('Manage fields' - * screen). - * @param $instance_edit - * $edit parameter for drupalPost() on the second step ('Instance settings' - * form). - */ - function fieldUIAddExistingField($bundle_path, $initial_edit, $instance_edit = array()) { - // Use 'test_field_widget' by default. - $initial_edit += array( - 'fields[_add_existing_field][widget_type]' => 'test_field_widget', - ); - $label = $initial_edit['fields[_add_existing_field][label]']; - $field_name = $initial_edit['fields[_add_existing_field][field_name]']; - - // First step : 'Add existing field' on the 'Manage fields' page. - $this->drupalPost("$bundle_path/fields", $initial_edit, t('Save')); - - // Second step : 'Instance settings' form. - $this->drupalPost(NULL, $instance_edit, t('Save settings')); - $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), t('Redirected to "Manage fields" page.')); - - // Check that the field appears in the overview form. - $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', $label, t('Field was created and appears in the overview page.')); + function setUp() { + parent::setUp(array('search')); } /** - * Delete a field instance through the Field UI. - * - * @param $bundle_path - * Path of the 'Manage fields' page for the bundle. - * @param $field_name - * The name of the field. - * @param $label - * The label of the field. - * @param $bundle_label - * The label of the bundle. + * Test formatter formatter settings. */ - function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_label) { - // Display confirmation form. - $this->drupalGet("$bundle_path/fields/$field_name/delete"); - $this->assertRaw(t('Are you sure you want to delete the field %label', array('%label' => $label)), t('Delete confirmation was found.')); + function testFormatterUI() { + $manage_fields = 'admin/structure/types/manage/' . $this->hyphen_type; + $manage_display = $manage_fields . '/display'; - // Submit confirmation form. - $this->drupalPost(NULL, array(), t('Delete')); - $this->assertRaw(t('The field %label has been deleted from the %type content type.', array('%label' => $label, '%type' => $bundle_label)), t('Delete message was found.')); + // Create a field, and a node with some data for the field. + $edit = array( + 'fields[_add_new_field][label]' => 'Test field', + 'fields[_add_new_field][field_name]' => 'field_test', + ); + $this->fieldUIAddNewField($manage_fields, $edit); - // Check that the field doesn not appear in the overview form - $this->assertNoFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $label, t('Field does not appear in the overview page.')); + // Clear the test-side cache and get the saved field instance. + field_info_cache_clear(); + $instance = field_info_instance('node', 'field_test', $this->type); + $format = $instance['display']['default']['type']; + $default_settings = field_info_formatter_settings($format); + $setting_name = key($default_settings); + $setting_value = $instance['display']['default']['settings'][$setting_name]; + + // Display the "Manage display" screen and check that the expected formatter is + // selected. + $this->drupalGet($manage_display); + $this->assertFieldByName('fields[field_test][type]', $format, t('The expected formatter is selected.')); + $this->assertText("$setting_name: $setting_value", t('The expected summary is displayed.')); + + // Change the formatter and check that the summary is updated. + $edit = array('fields[field_test][type]' => 'field_test_multiple', 'refresh_rows' => 'field_test'); + $this->drupalPostAJAX(NULL, $edit, array('op' => t('Refresh'))); + $format = 'field_test_multiple'; + $default_settings = field_info_formatter_settings($format); + $setting_name = key($default_settings); + $setting_value = $default_settings[$setting_name]; + $this->assertFieldByName('fields[field_test][type]', $format, t('The expected formatter is selected.')); + $this->assertText("$setting_name: $setting_value", t('The expected summary is displayed.')); + + // Submit the form and check that the instance is updated. + $this->drupalPost(NULL, array(), t('Save')); + field_info_cache_clear(); + $instance = field_info_instance('node', 'field_test', $this->type); + $current_format = $instance['display']['default']['type']; + $current_setting_value = $instance['display']['default']['settings'][$setting_name]; + $this->assertEqual($current_format, $format, t('The formatter was updated.')); + $this->assertEqual($current_setting_value, $setting_value, t('The setting was updated.')); } } diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index 61c37b1e345596e34cf6341136f948fec2f0d649..3f2350fe4dad49349478c1748acd26b11bbb1ce3 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -1,5 +1,5 @@ <?php -// $Id: file.field.inc,v 1.35 2010/10/01 01:32:59 webchick Exp $ +// $Id: file.field.inc,v 1.37 2010/10/20 15:22:53 dries Exp $ /** * @file @@ -649,7 +649,8 @@ function file_field_widget_process($element, &$form_state, $form) { // file, the entire group of file fields is updated together. if ($field['cardinality'] != 1) { $new_path = preg_replace('/\/\d+\//', '/', $element['remove_button']['#ajax']['path'], 1); - $new_wrapper = preg_replace('/-\d+-/', '-', $element['remove_button']['#ajax']['wrapper'], 1); + $field_element = drupal_array_get_nested_value($form, array_slice($element['#array_parents'], 0, -1)); + $new_wrapper = $field_element['#id'] . '-ajax-wrapper'; foreach (element_children($element) as $key) { if (isset($element[$key]['#ajax'])) { $element[$key]['#ajax']['path'] = $new_path; @@ -659,6 +660,15 @@ function file_field_widget_process($element, &$form_state, $form) { unset($element['#prefix'], $element['#suffix']); } + // Add another submit handler to the upload and remove buttons, to implement + // functionality needed by the field widget. This submit handler, along with + // the rebuild logic in file_field_widget_form() requires the entire field, + // not just the individual item, to be valid. + foreach (array('upload_button', 'remove_button') as $key) { + $element[$key]['#submit'][] = 'file_field_widget_submit'; + $element[$key]['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1)); + } + return $element; } @@ -674,8 +684,11 @@ function file_field_widget_process_multiple($element, &$form_state, $form) { foreach ($element_children as $delta => $key) { if ($key != $element['#file_upload_delta']) { + $description = _file_field_get_description_from_element($element[$key]); $element[$key]['_weight'] = array( '#type' => 'weight', + '#title' => $description ? t('Weight for @title', array('@title' => $description)) : t('Weight for new file'), + '#title_display' => 'invisible', '#delta' => $count, '#default_value' => $delta, ); @@ -698,6 +711,46 @@ function file_field_widget_process_multiple($element, &$form_state, $form) { return $element; } +/** + * Helper function for file_field_widget_process_multiple(). + * + * @param $element + * The element being processed. + * @return + * A description of the file suitable for use in the administrative interface. + */ +function _file_field_get_description_from_element($element) { + // Use the actual file description, if it's available. + if (!empty($element['#default_value']['description'])) { + return $element['#default_value']['description']; + } + // Otherwise, fall back to the filename. + if (!empty($element['#default_value']['filename'])) { + return $element['#default_value']['filename']; + } + // This is probably a newly uploaded file; no description is available. + return FALSE; +} + +/** + * Submit handler for upload and remove buttons of file_generic fields. + * + * This runs in addition to and after file_managed_file_submit(). + * + * @see file_managed_file_submit() + * @see file_field_widget_form() + * @see file_field_widget_process() + */ +function file_field_widget_submit($form, &$form_state) { + // During the form rebuild, file_field_widget_form() will create field item + // widget elements using re-indexed deltas, so clear out $form_state['input'] + // to avoid a mismatch between old and new deltas. The rebuilt elements will + // have #default_value set appropriately for the current state of the field, + // so nothing is lost in doing this. + list($field_name, $langcode) = $form_state['triggering_element']['#parents']; + unset($form_state['input'][$field_name][$langcode]); +} + /** * Returns HTML for an individual file upload widget. * diff --git a/modules/file/file.info b/modules/file/file.info index 7c1c7d96ae7bf6059b5529d582ae6ad6e7179b2b..12cb50fec9935f32665540eb1c8b76250939d15a 100644 --- a/modules/file/file.info +++ b/modules/file/file.info @@ -10,8 +10,8 @@ files[] = file.field.inc files[] = file.install files[] = tests/file.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/file/file.module b/modules/file/file.module index 73d54930dca6f3d45d5c865962dd6a6a4a9fefd7..a1b4450fa4ba2a0b52856af9a818bdaf339c2143 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -1,5 +1,5 @@ <?php -// $Id: file.module,v 1.42 2010/10/04 17:46:01 dries Exp $ +// $Id: file.module,v 1.45 2010/10/20 15:22:53 dries Exp $ /** * @file @@ -216,24 +216,8 @@ function file_file_download($uri, $field_type = 'file') { } // Access is granted. - $name = mime_header_encode($file->filename); - $type = mime_header_encode($file->filemime); - // Serve images, text, and flash content for display rather than download. - $inline_types = variable_get('file_inline_types', array('^text/', '^image/', 'flash$')); - $disposition = 'attachment'; - foreach ($inline_types as $inline_type) { - // Exclamation marks are used as delimiters to avoid escaping slashes. - if (preg_match('!' . $inline_type . '!', $file->filemime)) { - $disposition = 'inline'; - } - } - - return array( - 'Content-Type' => $type . '; name="' . $name . '"', - 'Content-Length' => $file->filesize, - 'Content-Disposition' => $disposition . '; filename="' . $name . '"', - 'Cache-Control' => 'private', - ); + $headers = file_get_content_headers($file); + return $headers; } /** @@ -392,6 +376,7 @@ function file_managed_file_process($element, &$form_state, $form) { '#value' => t('Upload'), '#validate' => array(), '#submit' => array('file_managed_file_submit'), + '#limit_validation_errors' => array($element['#parents']), '#ajax' => $ajax_settings, '#weight' => -5, ); @@ -405,16 +390,11 @@ function file_managed_file_process($element, &$form_state, $form) { '#value' => t('Remove'), '#validate' => array(), '#submit' => array('file_managed_file_submit'), + '#limit_validation_errors' => array($element['#parents']), '#ajax' => $ajax_settings, '#weight' => -5, ); - // Limit validation errors to the file field only. The entire field is needed later - // by file_field_widget_form(), so the last element is sliced off the #parents array - // to avoid removing too much from $form_state['values']. - $element['upload_button']['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1)); - $element['remove_button']['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1)); - $element['fid'] = array( '#type' => 'hidden', '#value' => $fid, @@ -447,6 +427,8 @@ function file_managed_file_process($element, &$form_state, $form) { $element['upload'] = array( '#name' => 'files[' . implode('_', $element['#parents']) . ']', '#type' => 'file', + '#title' => t('Choose a file'), + '#title_display' => 'invisible', '#size' => 22, '#theme_wrappers' => array(), '#weight' => -10, diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index 177aaa1efc64176b6e979155b7480cd5b983784c..464a8125d29b6559fbb4a04b48be1210e8bdd03c 100644 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -1,5 +1,5 @@ <?php -// $Id: file.test,v 1.26 2010/10/01 01:32:59 webchick Exp $ +// $Id: file.test,v 1.27 2010/10/20 15:22:53 dries Exp $ /** * @file @@ -203,29 +203,21 @@ class FileFieldTestCase extends DrupalWebTestCase { /** - * Test class to test file field upload and remove buttons, with and without AJAX. + * Test class to test file field widget, single and multi-valued, with and without AJAX, with public and private files. */ class FileFieldWidgetTestCase extends FileFieldTestCase { public static function getInfo() { return array( 'name' => 'File field widget test', - 'description' => 'Test upload and remove buttons, with and without AJAX.', + 'description' => 'Tests the file field widget, single and multi-valued, with and without AJAX, with public and private files.', 'group' => 'File', ); } /** - * Tests upload and remove buttons, with and without AJAX. - * - * @todo This function currently only tests the "remove" button of a single- - * valued field. Tests should be added for the "upload" button and for each - * button of a multi-valued field. Tests involving multiple AJAX steps on - * the same page will become easier after http://drupal.org/node/789186 - * lands. Testing the "upload" button in AJAX context requires more - * investigation into how jQuery uploads files, so that drupalPostAJAX() can - * emulate that correctly. + * Tests upload and remove buttons, with and without AJAX, for a single-valued File field. */ - function testWidget() { + function testSingleValuedWidget() { // Use 'page' instead of 'article', so that the 'article' image field does // not conflict with this test. If in the future the 'page' type gets its // own default file or image field, this test can be made more robust by @@ -241,11 +233,14 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { foreach (array('nojs', 'js') as $type) { // Create a new node with the uploaded file and ensure it got uploaded // successfully. + // @todo This only tests a 'nojs' submission, because drupalPostAJAX() + // does not yet support file uploads. $nid = $this->uploadNodeFile($test_file, $field_name, $type_name); $node = node_load($nid, NULL, TRUE); $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0]; $this->assertFileExists($node_file, t('New file saved to disk on node creation.')); - // Test file download. + + // Ensure the file can be downloaded. $this->drupalGet(file_create_url($node_file->uri)); $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.')); @@ -260,13 +255,8 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { $this->drupalPost(NULL, array(), t('Remove')); break; case 'js': - // @todo This can be simplified after http://drupal.org/node/789186 - // lands. - preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches); - $settings = drupal_json_decode($matches[1]); $button = $this->xpath('//input[@type="submit" and @value="' . t('Remove') . '"]'); - $button_id = (string) $button[0]['id']; - $this->drupalPostAJAX(NULL, array(), array((string) $button[0]['name'] => (string) $button[0]['value']), $settings['ajax'][$button_id]['url'], array(), array(), NULL, $settings['ajax'][$button_id]); + $this->drupalPostAJAX(NULL, array(), array((string) $button[0]['name'] => (string) $button[0]['value'])); break; } @@ -279,34 +269,133 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { $node = node_load($nid, NULL, TRUE); $this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), t('File was successfully removed from the node.')); } + } - // Test partial form submissions using the Upload button on a multivalue field. - field_delete_field($field_name); + /** + * Tests upload and remove buttons, with and without AJAX, for a multi-valued File field. + */ + function testMultiValuedWidget() { + // Use 'page' instead of 'article', so that the 'article' image field does + // not conflict with this test. If in the future the 'page' type gets its + // own default file or image field, this test can be made more robust by + // using a custom node type. + $type_name = 'page'; + $field_name = strtolower($this->randomName()); $this->createFileField($field_name, $type_name, array('cardinality' => 3)); + $field = field_info_field($field_name); + $instance = field_info_instance('node', $field_name, $type_name); + + $test_file = $this->getTestFile('text'); + + foreach (array('nojs', 'js') as $type) { + // Visit the node creation form, and upload 3 files. Since the field has + // cardinality of 3, ensure the "Upload" button is displayed until after + // the 3rd file, and after that, isn't displayed. + // @todo This is only testing a non-AJAX upload, because drupalPostAJAX() + // does not yet emulate jQuery's file upload. + $this->drupalGet("node/add/$type_name"); + for ($delta = 0; $delta < 3; $delta++) { + $edit = array('files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri)); + // If the Upload button doesn't exist, drupalPost() will automatically + // fail with an assertion message. + $this->drupalPost(NULL, $edit, t('Upload')); + } + $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files, the "Upload" button is no longer displayed.')); + + // Test clicking each "Remove" button. For extra robustness, test them out + // of sequential order. They are 0-indexed, and get renumbered after each + // iteration, so array(1, 1, 0) means: + // - First remove the 2nd file. + // - Then remove what is then the 2nd file (was originally the 3rd file). + // - Then remove the first file. + $num_expected_remove_buttons = 3; + foreach (array(1, 1, 0) as $delta) { + // Ensure we have the expected number of Remove buttons, and that they + // are numbered sequentially. + $buttons = $this->xpath('//input[@type="submit" and @value="Remove"]'); + $this->assertTrue(is_array($buttons) && count($buttons) === $num_expected_remove_buttons, t('There are %n "Remove" buttons displayed (JSMode=%type).', array('%n' => $num_expected_remove_buttons, '%type' => $type))); + foreach ($buttons as $i => $button) { + $this->assertIdentical((string) $button['name'], $field_name . '_' . LANGUAGE_NONE . '_' . $i . '_remove_button'); + } + + // "Click" the remove button (emulating either a nojs or js submission). + $button_name = $field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button'; + switch ($type) { + case 'nojs': + // drupalPost() takes a $submit parameter that is the value of the + // button whose click we want to emulate. Since we have multiple + // buttons with the value "Remove", and want to control which one we + // use, we change the value of the other ones to something else. + // Since non-clicked buttons aren't included in the submitted POST + // data, and since drupalPost() will result in $this being updated + // with a newly rebuilt form, this doesn't cause problems. + foreach ($buttons as $button) { + if ($button['name'] != $button_name) { + $button['value'] = 'DUMMY'; + } + } + $this->drupalPost(NULL, array(), t('Remove')); + break; + case 'js': + // drupalPostAJAX() lets us target the button precisely, so we don't + // require the workaround used above for nojs. + $this->drupalPostAJAX(NULL, array(), array($button_name => t('Remove'))); + break; + } + $num_expected_remove_buttons--; + + // Ensure we have a single Upload button, and that it is numbered + // sequentially after the Remove buttons. + $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]'); + $this->assertTrue(is_array($buttons) && count($buttons) == 1 && ((string) $buttons[0]['name'] === ($field_name . '_' . LANGUAGE_NONE . '_' . $num_expected_remove_buttons . '_upload_button')), t('After removing a file, an "Upload" button is displayed (JSMode=%type).')); + } - $this->drupalGet("node/add/$type_name"); - for ($delta = 0; $delta < 3; $delta++) { - $edit = array('files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri)); - $this->drupalPost(NULL, $edit, t('Upload')); + // Ensure the page now has no Remove buttons. + $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After removing all files, there is no "Remove" button displayed.', array('%n' => $num_expected_remove_buttons, '%type' => $type))); + + // Save the node and ensure it does not have any files. + $this->drupalPost(NULL, array('title' => $this->randomName()), t('Save')); + $matches = array(); + preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches); + $nid = $matches[1]; + $node = node_load($nid, NULL, TRUE); + $this->assertTrue(empty($node->{$field_name}[LANGUAGE_NONE][0]['fid']), t('Node was successfully saved without any files.')); } - $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files, the "Upload" button is no longer displayed.')); + } + + /** + * Tests a file field with a "Private files" upload destination setting. + */ + function testPrivateFileSetting() { + // Use 'page' instead of 'article', so that the 'article' image field does + // not conflict with this test. If in the future the 'page' type gets its + // own default file or image field, this test can be made more robust by + // using a custom node type. + $type_name = 'page'; + $field_name = strtolower($this->randomName()); + $this->createFileField($field_name, $type_name); + $field = field_info_field($field_name); + $instance = field_info_instance('node', $field_name, $type_name); - // Test private download method. + $test_file = $this->getTestFile('text'); + + // Change the field setting to make its files private, and upload a file. $edit = array('field[settings][uri_scheme]' => 'private'); $this->drupalPost("admin/structure/types/manage/$type_name/fields/$field_name", $edit, t('Save settings')); - // Create a new node with the uploaded file and ensure it got uploaded - // successfully. $nid = $this->uploadNodeFile($test_file, $field_name, $type_name); $node = node_load($nid, NULL, TRUE); $node_file = (object) $node->{$field_name}[LANGUAGE_NONE][0]; $this->assertFileExists($node_file, t('New file saved to disk on node creation.')); - // Test file download. + + // Ensure the private file is available to the user who uploaded it. $this->drupalGet(file_create_url($node_file->uri)); $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.')); + // Ensure we can't change 'uri_scheme' field settings while there are some // entities with uploaded files. $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_name"); $this->assertFieldByXpath('//input[@id="edit-field-settings-uri-scheme-public" and @disabled="disabled"]', 'public', t('Upload destination setting disabled.')); + // Delete node and confirm that setting could be changed. node_delete($nid); $this->drupalGet("admin/structure/types/manage/$type_name/fields/$field_name"); diff --git a/modules/file/tests/file_module_test.info b/modules/file/tests/file_module_test.info index 08bb0079dc64cc5eef2236d1d778935e003793ec..7a3a45f72ce04cc1d95258f2d12be521d8526b55 100644 --- a/modules/file/tests/file_module_test.info +++ b/modules/file/tests/file_module_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = file_module_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/filter/filter.admin.inc b/modules/filter/filter.admin.inc index a1c841733e40ceb67a086049d1ed5b4a97f63a5f..a75c0d485c30515791c18aec0ca200e583b2f2e5 100644 --- a/modules/filter/filter.admin.inc +++ b/modules/filter/filter.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: filter.admin.inc,v 1.66 2010/09/29 19:52:12 dries Exp $ +// $Id: filter.admin.inc,v 1.69 2010/10/22 16:36:14 webchick Exp $ /** * @file @@ -34,7 +34,12 @@ function filter_admin_overview($form) { $form['formats'][$id]['roles'] = array('#markup' => $roles_markup); $form['formats'][$id]['configure'] = array('#type' => 'link', '#title' => t('configure'), '#href' => 'admin/config/content/formats/' . $id); $form['formats'][$id]['disable'] = array('#type' => 'link', '#title' => t('disable'), '#href' => 'admin/config/content/formats/' . $id . '/disable', '#access' => !$form['formats'][$id]['#is_fallback']); - $form['formats'][$id]['weight'] = array('#type' => 'weight', '#default_value' => $format->weight); + $form['formats'][$id]['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $format->name)), + '#title_display' => 'invisible', + '#default_value' => $format->weight, + ); } $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save changes')); @@ -96,7 +101,10 @@ function theme_filter_admin_overview($variables) { function filter_admin_format_page($format = NULL) { if (!isset($format->name)) { drupal_set_title(t('Add text format')); - $format = (object) array('name' => '', 'format' => 0); + $format = (object) array( + 'format' => NULL, + 'name' => '', + ); } return drupal_get_form('filter_admin_format_form', $format); } @@ -122,6 +130,16 @@ function filter_admin_format_form($form, &$form_state, $format) { '#default_value' => $format->name, '#required' => TRUE, ); + $form['format'] = array( + '#type' => 'machine_name', + '#required' => TRUE, + '#default_value' => $format->format, + '#maxlength' => 255, + '#machine_name' => array( + 'exists' => 'filter_format_exists', + ), + '#disabled' => !empty($format->format), + ); // Add user role access selection. $form['roles'] = array( @@ -191,6 +209,8 @@ function filter_admin_format_form($form, &$form_state, $format) { ); $form['filters']['order'][$name]['weight'] = array( '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $filter['title'])), + '#title_display' => 'invisible', '#delta' => 50, '#default_value' => $filters[$name]->weight, '#parents' => array('filters', $name, 'weight'), @@ -227,9 +247,6 @@ function filter_admin_format_form($form, &$form_state, $format) { } } - if (!empty($format->format)) { - $form['format'] = array('#type' => 'value', '#value' => $format->format); - } $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); @@ -271,13 +288,16 @@ function theme_filter_admin_format_filter_order($variables) { * Validate text format form submissions. */ function filter_admin_format_form_validate($form, &$form_state) { - if (!isset($form_state['values']['format'])) { - $format_name = trim($form_state['values']['name']); - form_set_value($form['name'], $format_name, $form_state); - $result = db_query("SELECT format FROM {filter_format} WHERE name = :name", array(':name' => $format_name))->fetchField(); - if ($result) { - form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name))); - } + $format_format = trim($form_state['values']['format']); + $format_name = trim($form_state['values']['name']); + + // Ensure that the values to be saved later are exactly the ones validated. + form_set_value($form['format'], $format_format, $form_state); + form_set_value($form['name'], $format_name, $form_state); + + $result = db_query("SELECT format FROM {filter_format} WHERE name = :name AND format <> :format", array(':name' => $format_name, ':format' => $format_format))->fetchField(); + if ($result) { + form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name))); } } @@ -290,9 +310,6 @@ function filter_admin_format_form_submit($form, &$form_state) { // Save text format. $format = (object) $form_state['values']; - if (!isset($form_state['values']['format'])) { - $format->format = NULL; - } $status = filter_format_save($format); // Save user permissions. diff --git a/modules/filter/filter.info b/modules/filter/filter.info index d233b76704808ea45cbda30feddbd0cb73134125..f353db36328fd08327e40d49dfd61b863d90f397 100644 --- a/modules/filter/filter.info +++ b/modules/filter/filter.info @@ -12,8 +12,8 @@ files[] = filter.test required = TRUE configure = admin/config/content/formats -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/filter/filter.install b/modules/filter/filter.install index fd82ae19499ecb2ede03efa44c012a080435b737..8a9d997bebffd6cd96a4a4d2cf033fd2939d7eda 100644 --- a/modules/filter/filter.install +++ b/modules/filter/filter.install @@ -1,5 +1,5 @@ <?php -// $Id: filter.install,v 1.46 2010/09/28 03:30:37 webchick Exp $ +// $Id: filter.install,v 1.49 2010/10/23 00:43:48 webchick Exp $ /** * @file @@ -14,9 +14,9 @@ function filter_schema() { 'description' => 'Table that maps filters (HTML corrector) to text formats (Filtered HTML).', 'fields' => array( 'format' => array( - 'type' => 'int', + 'type' => 'varchar', + 'length' => 255, 'not null' => TRUE, - 'default' => 0, 'description' => 'Foreign key: The {filter_format}.format to which this filter is assigned.', ), 'module' => array( @@ -62,9 +62,10 @@ function filter_schema() { 'description' => 'Stores text formats: custom groupings of filters, such as Filtered HTML.', 'fields' => array( 'format' => array( - 'type' => 'serial', + 'type' => 'varchar', + 'length' => 255, 'not null' => TRUE, - 'description' => 'Primary Key: Unique ID for format.', + 'description' => 'Primary Key: Unique machine name of the format.', ), 'name' => array( 'type' => 'varchar', @@ -120,6 +121,7 @@ function filter_install() { // plain text format with very basic formatting, but it can be modified by // installation profiles to have other properties. $plain_text_format = array( + 'format' => 'plain_text', 'name' => 'Plain text', 'weight' => 10, 'filters' => array( @@ -469,6 +471,24 @@ function filter_update_7009() { db_create_table('cache_filter', $schema); } +/** + * Change {filter_format}.format and {filter}.format into varchar. + */ +function filter_update_7010() { + db_change_field('filter_format', 'format', 'format', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Primary Key: Unique machine name of the format.', + )); + db_change_field('filter', 'format', 'format', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Foreign key: The {filter_format}.format to which this filter is assigned.', + )); +} + /** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/filter/filter.module b/modules/filter/filter.module index aed3c218988ec279265617b45ab7de8e8a7244c7..a19acb6f2152808e4cb6942079368eaa4cd462f3 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -1,5 +1,5 @@ <?php -// $Id: filter.module,v 1.351 2010/10/04 18:00:45 dries Exp $ +// $Id: filter.module,v 1.355 2010/10/22 16:36:14 webchick Exp $ /** * @file @@ -153,7 +153,11 @@ function _filter_disable_format_access($format) { * The format ID. * * @return - * A fully-populated text format object. + * A fully-populated text format object, if the requested format exists and + * is enabled. If the format does not exist, or exists in the database but + * has been marked as disabled, FALSE is returned. + * + * @see filter_format_exists() */ function filter_format_load($format_id) { $formats = filter_formats(); @@ -165,9 +169,10 @@ function filter_format_load($format_id) { * * @param $format * A format object using the properties: + * - 'format': A machine-readable name representing the ID of the text format + * to save. If this corresponds to an existing text format, that format + * will be updated; otherwise, a new format will be created. * - 'name': The title of the text format. - * - 'format': (optional) The internal ID of the text format. If omitted, a - * new text format is created. * - 'weight': (optional) The weight of the text format, which controls its * placement in text format lists. If omitted, the weight is set to 0. * - 'filters': (optional) An associative, multi-dimensional array of filters @@ -182,23 +187,31 @@ function filter_format_load($format_id) { * - 'settings': (optional) An array of configured settings for the filter. * See hook_filter_info() for details. */ -function filter_format_save(&$format) { +function filter_format_save($format) { $format->name = trim($format->name); $format->cache = _filter_format_is_cacheable($format); - $format->status = 1; + if (!isset($format->status)) { + $format->status = 1; + } + if (!isset($format->weight)) { + $format->weight = 0; + } + + // Insert or update the text format. + $return = db_merge('filter_format') + ->key(array('format' => $format->format)) + ->fields(array( + 'name' => $format->name, + 'cache' => (int) $format->cache, + 'status' => (int) $format->status, + 'weight' => (int) $format->weight, + )) + ->execute(); + // Programmatic saves may not contain any filters. if (!isset($format->filters)) { $format->filters = array(); } - - // Add a new text format. - if (empty($format->format)) { - $return = drupal_write_record('filter_format', $format); - } - else { - $return = drupal_write_record('filter_format', $format, 'format'); - } - $filter_info = filter_get_filters(); foreach ($filter_info as $name => $filter) { // Add new filters without weight to the bottom. @@ -241,8 +254,8 @@ function filter_format_save(&$format) { module_invoke_all('filter_format_update', $format); // Explicitly indicate that the format was updated. We need to do this // since if the filters were updated but the format object itself was not, - // the call to drupal_write_record() above would not return an indication - // that anything had changed. + // the merge query above would not return an indication that anything had + // changed. $return = SAVED_UPDATED; // Clear the filter cache whenever a text format is updated. @@ -258,9 +271,9 @@ function filter_format_save(&$format) { * Disable a text format. * * There is no core facility to re-enable a disabled format. It is not deleted - * to keep information for contrib and to make sure the format auto increment - * id is never reused. As there might be content using the disabled format, - * this would lead to data corruption. + * to keep information for contrib and to make sure the format ID is never + * reused. As there might be content using the disabled format, this would lead + * to data corruption. * * @param $format * The text format object to be disabled. @@ -279,6 +292,23 @@ function filter_format_disable($format) { cache_clear_all($format->format . ':', 'cache_filter', TRUE); } +/** + * Determines if a text format exists. + * + * @param $format_id + * The ID of the text format to check. + * + * @return + * TRUE if the text format exists, FALSE otherwise. Note that for disabled + * formats filter_format_exists() will return TRUE while filter_format_load() + * will return FALSE. + * + * @see filter_format_load() + */ +function filter_format_exists($format_id) { + return (bool) db_query_range('SELECT 1 FROM {filter_format} WHERE format = :format', 0, 1, array(':format' => $format_id))->fetchField(); +} + /** * Display a text format form title. */ @@ -368,17 +398,25 @@ function filter_modules_disabled($modules) { * @see filter_formats_reset() */ function filter_formats($account = NULL) { + global $language; $formats = &drupal_static(__FUNCTION__, array()); - // Statically cache all existing formats upfront. + // All available formats are cached for performance. if (!isset($formats['all'])) { - $formats['all'] = db_select('filter_format', 'ff') - ->addTag('translatable') - ->fields('ff') - ->condition('status', 1) - ->orderBy('weight') - ->execute() - ->fetchAllAssoc('format'); + if ($cache = cache_get("filter_formats:{$language->language}")) { + $formats['all'] = $cache->data; + } + else { + $formats['all'] = db_select('filter_format', 'ff') + ->addTag('translatable') + ->fields('ff') + ->condition('status', 1) + ->orderBy('weight') + ->execute() + ->fetchAllAssoc('format'); + + cache_set("filter_formats:{$language->language}", $formats['all']); + } } // Build a list of user-specific formats. @@ -395,11 +433,13 @@ function filter_formats($account = NULL) { } /** - * Resets the static cache of all text formats. + * Resets text format caches. * * @see filter_formats() */ function filter_formats_reset() { + cache_clear_all('filter_formats', 'cache', TRUE); + cache_clear_all('filter_list_format', 'cache', TRUE); drupal_static_reset('filter_list_format'); drupal_static_reset('filter_formats'); } @@ -507,7 +547,7 @@ function filter_default_format($account = NULL) { function filter_fallback_format() { // This variable is automatically set in the database for all installations // of Drupal. In the event that it gets disabled or deleted somehow, there - // is no safe default to return, since we do not want to risk making an + // is no safe default to return, since we do not want to risk making an // existing (and potentially unsafe) text format on the site automatically // available to all users. Returning NULL at least guarantees that this // cannot happen. @@ -625,9 +665,15 @@ function filter_list_format($format_id) { $filter_info = filter_get_filters(); if (!isset($filters['all'])) { - $result = db_query('SELECT * FROM {filter} ORDER BY weight, module, name'); - foreach ($result as $record) { - $filters['all'][$record->format][$record->name] = $record; + if ($cache = cache_get('filter_list_format')) { + $filters['all'] = $cache->data; + } + else { + $result = db_query('SELECT * FROM {filter} ORDER BY weight, module, name'); + foreach ($result as $record) { + $filters['all'][$record->format][$record->name] = $record; + } + cache_set('filter_list_format', $filters['all']); } } @@ -1102,7 +1148,7 @@ function theme_filter_guidelines($variables) { } /** - * @name Standard filters + * @defgroup standard_filters Standard filters * @{ * Filters implemented by the filter.module. */ diff --git a/modules/filter/filter.test b/modules/filter/filter.test index 54145a62f6ca7a20948546f329957639201d6c0e..8225d36838cfcb8d3a534843a04ef6f84f83acff 100644 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -1,5 +1,5 @@ <?php -// $Id: filter.test,v 1.77 2010/09/28 03:30:37 webchick Exp $ +// $Id: filter.test,v 1.80 2010/10/23 02:26:11 webchick Exp $ /** * Tests for text format and filter CRUD operations. @@ -23,6 +23,7 @@ class FilterCRUDTestCase extends DrupalWebTestCase { function testTextFormatCRUD() { // Add a text format with minimum data only. $format = new stdClass(); + $format->format = 'empty_format'; $format->name = 'Empty format'; filter_format_save($format); $this->verifyTextFormat($format); @@ -30,6 +31,7 @@ class FilterCRUDTestCase extends DrupalWebTestCase { // Add another text format specifying all possible properties. $format = new stdClass(); + $format->format = 'custom_format'; $format->name = 'Custom format'; $format->filters = array( 'filter_url' => array( @@ -184,6 +186,7 @@ class FilterAdminTestCase extends DrupalWebTestCase { $this->drupalGet('admin/config/content/formats'); $this->clickLink('Add text format'); $edit = array( + 'format' => drupal_strtolower($this->randomName()), 'name' => $this->randomName(), ); $this->drupalPost(NULL, $edit, t('Save configuration')); @@ -204,6 +207,24 @@ class FilterAdminTestCase extends DrupalWebTestCase { // Verify that disabled text format no longer exists. $this->drupalGet('admin/config/content/formats/' . $format->format); $this->assertResponse(404, t('Disabled text format no longer exists.')); + + // Attempt to create a format of the same machine name as the disabled + // format but with a different human readable name. + $edit = array( + 'format' => $format->format, + 'name' => 'New format', + ); + $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); + $this->assertText('The machine-readable name is already in use. It must be unique.'); + + // Attempt to create a format of the same human readable name as the + // disabled format but with a different machine name. + $edit = array( + 'format' => 'new_format', + 'name' => $format->name, + ); + $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); + $this->assertText('Text format names must be unique. A format named ' . check_plain($format->name) . ' already exists.'); } /** @@ -268,6 +289,7 @@ class FilterAdminTestCase extends DrupalWebTestCase { // Add format. $edit = array(); + $edit['format'] = drupal_strtolower($this->randomName()); $edit['name'] = $this->randomName(); $edit['roles[2]'] = 1; $edit['filters[' . $second_filter . '][status]'] = TRUE; @@ -424,7 +446,10 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase { $this->drupalLogin($this->filter_admin_user); $formats = array(); for ($i = 0; $i < 2; $i++) { - $edit = array('name' => $this->randomName()); + $edit = array( + 'format' => drupal_strtolower($this->randomName()), + 'name' => $this->randomName(), + ); $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); $this->resetFilterCaches(); $format_id = db_query("SELECT format FROM {filter_format} WHERE name = :name", array(':name' => $edit['name']))->fetchField(); @@ -472,9 +497,18 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase { // the disallowed format does not. $this->drupalLogin($this->web_user); $this->drupalGet('node/add/page'); - $this->assertRaw($this->formatSelectorHTML($this->allowed_format), t('The allowed text format appears as an option when adding a new node.')); - $this->assertNoRaw($this->formatSelectorHTML($this->disallowed_format), t('The disallowed text format does not appear as an option when adding a new node.')); - $this->assertRaw($this->formatSelectorHTML(filter_format_load(filter_fallback_format())), t('The fallback format appears as an option when adding a new node.')); + $langcode = LANGUAGE_NONE; + $elements = $this->xpath('//select[@name=:name]/option', array( + ':name' => "body[$langcode][0][format]", + ':option' => $this->allowed_format->format, + )); + $options = array(); + foreach ($elements as $element) { + $options[(string) $element['value']] = $element; + } + $this->assertTrue(isset($options[$this->allowed_format->format]), t('The allowed text format appears as an option when adding a new node.')); + $this->assertFalse(isset($options[$this->disallowed_format->format]), t('The disallowed text format does not appear as an option when adding a new node.')); + $this->assertTrue(isset($options[filter_fallback_format()]), t('The fallback format appears as an option when adding a new node.')); } function testFormatRoles() { @@ -619,18 +653,6 @@ class FilterFormatAccessTestCase extends DrupalWebTestCase { $this->assertNoText($old_title, t('Old title not found.')); } - /** - * Returns the expected HTML for a particular text format selector. - * - * @param $format - * An object representing the text format for which to return HTML. - * @return - * The expected HTML for that text format's selector. - */ - function formatSelectorHTML($format) { - return '<option value="' . $format->format . '">' . $format->name . '</option>'; - } - /** * Rebuild text format and permission caches in the thread running the tests. */ @@ -656,7 +678,10 @@ class FilterDefaultFormatTestCase extends DrupalWebTestCase { $this->drupalLogin($admin_user); $formats = array(); for ($i = 0; $i < 2; $i++) { - $edit = array('name' => $this->randomName()); + $edit = array( + 'format' => drupal_strtolower($this->randomName()), + 'name' => $this->randomName(), + ); $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); $this->resetFilterCaches(); $format_id = db_query("SELECT format FROM {filter_format} WHERE name = :name", array(':name' => $edit['name']))->fetchField(); @@ -1072,7 +1097,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase { $f = _filter_html('<p onerror="alert(0);" />', $filter); $this->assertNoNormalized($f, 'onerror', t('HTML filter should remove on* attributes on default.')); - $f = _filter_html('<code onerror> </code>', $filter); + $f = _filter_html('<code onerror> </code>', $filter); $this->assertNoNormalized($f, 'onerror', t('HTML filter should remove empty on* attributes on default.')); } @@ -1684,6 +1709,7 @@ class FilterHooksTestCase extends DrupalWebTestCase { // Add a text format. $name = $this->randomName(); $edit = array(); + $edit['format'] = drupal_strtolower($this->randomName()); $edit['name'] = $name; $edit['roles[1]'] = 1; $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration')); diff --git a/modules/forum/forum.info b/modules/forum/forum.info index 3b5cca783bb3565f88e59fd3cd73951efa250870..566f06cb9d7e6305faae5484552ccaead53cda79 100644 --- a/modules/forum/forum.info +++ b/modules/forum/forum.info @@ -14,8 +14,8 @@ files[] = forum.test configure = admin/structure/forum stylesheets[all][] = forum.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/help/help.info b/modules/help/help.info index f6201852d765357e1f3757fd91665a5de3f59ab0..d3bd19166b4ffd07b8cca99bced71c92670589bb 100644 --- a/modules/help/help.info +++ b/modules/help/help.info @@ -8,8 +8,8 @@ files[] = help.module files[] = help.admin.inc files[] = help.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/image/image.admin.inc b/modules/image/image.admin.inc index 40c892c5867c278870c0691a8124acd4cbb5cf9d..4aae2d2f5c93cac32242213efba804707b42b4df 100644 --- a/modules/image/image.admin.inc +++ b/modules/image/image.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: image.admin.inc,v 1.25 2010/10/04 18:00:45 dries Exp $ +// $Id: image.admin.inc,v 1.27 2010/10/21 20:50:43 webchick Exp $ /** * @file @@ -93,6 +93,8 @@ function image_style_form($form, &$form_state, $style) { ); $form['effects'][$key]['weight'] = array( '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $effect['label'])), + '#title_display' => 'invisible', '#default_value' => $effect['weight'], '#access' => $editable, ); @@ -127,11 +129,15 @@ function image_style_form($form, &$form_state, $style) { ); $form['effects']['new']['new'] = array( '#type' => 'select', + '#title' => t('Effect'), + '#title_display' => 'invisible', '#options' => $new_effect_options, '#empty_option' => t('Select a new effect'), ); $form['effects']['new']['weight'] = array( '#type' => 'weight', + '#title' => t('Weight for new effect'), + '#title_display' => 'invisible', '#default_value' => count($form['effects']) - 1, ); $form['effects']['new']['add'] = array( @@ -786,24 +792,25 @@ function theme_image_style_preview($variables) { $preview_attributes['style'] = 'width: ' . $preview_width . 'px; height: ' . $preview_height . 'px;'; // In the previews, timestamps are added to prevent caching of images. - $output = ''; - $output .= '<div class="image-style-preview preview clearfix">'; + $output = '<div class="image-style-preview preview clearfix">'; // Build the preview of the original image. + $original_url = file_create_url($original_path); $output .= '<div class="preview-image-wrapper">'; - $output .= t('original') . ' (' . l(t('view actual size'), $original_path) . ')'; + $output .= t('original') . ' (' . l(t('view actual size'), $original_url) . ')'; $output .= '<div class="preview-image original-image" style="' . $original_attributes['style'] . '">'; - $output .= '<a href="' . url($original_path) . '?' . time() . '">' . theme('image', array('path' => $original_path . '?' . time(), 'alt' => t('Sample original image'), 'title' => '', 'attributes' => $original_attributes)) . '</a>'; + $output .= '<a href="' . $original_url . '">' . theme('image', array('path' => $original_path, 'alt' => t('Sample original image'), 'title' => '', 'attributes' => $original_attributes)) . '</a>'; $output .= '<div class="height" style="height: ' . $original_height . 'px"><span>' . $original_image['height'] . 'px</span></div>'; $output .= '<div class="width" style="width: ' . $original_width . 'px"><span>' . $original_image['width'] . 'px</span></div>'; $output .= '</div>'; // End preview-image. $output .= '</div>'; // End preview-image-wrapper. // Build the preview of the image style. + $preview_url = file_create_url($preview_file) . '?cache_bypass=' . REQUEST_TIME; $output .= '<div class="preview-image-wrapper">'; $output .= check_plain($style['name']) . ' (' . l(t('view actual size'), file_create_url($preview_file) . '?' . time()) . ')'; $output .= '<div class="preview-image modified-image" style="' . $preview_attributes['style'] . '">'; - $output .= '<a href="' . file_create_url($preview_file) . '?' . time() . '">' . theme('image', array('path' => file_create_url($preview_file) . '?' . time(), 'alt' => t('Sample modified image'), 'title' => '', 'attributes' => $preview_attributes)) . '</a>'; + $output .= '<a href="' . file_create_url($preview_file) . '?' . time() . '">' . theme('image', array('path' => $preview_url, 'alt' => t('Sample modified image'), 'title' => '', 'attributes' => $preview_attributes)) . '</a>'; $output .= '<div class="height" style="height: ' . $preview_height . 'px"><span>' . $preview_image['height'] . 'px</span></div>'; $output .= '<div class="width" style="width: ' . $preview_width . 'px"><span>' . $preview_image['width'] . 'px</span></div>'; $output .= '</div>'; // End preview-image. diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc index 7e5daa6241ae44fa7c7d65c4c16fb0699f879e41..d73ee5998121668afaa8d8d8d89556ddf218d645 100644 --- a/modules/image/image.field.inc +++ b/modules/image/image.field.inc @@ -1,5 +1,5 @@ <?php -// $Id: image.field.inc,v 1.30 2010/10/04 18:00:45 dries Exp $ +// $Id: image.field.inc,v 1.33 2010/10/22 00:42:42 dries Exp $ /** * @file @@ -86,6 +86,8 @@ function image_field_instance_settings_form($field, $instance) { ); $form['max_resolution']['x'] = array( '#type' => 'textfield', + '#title' => t('Maximum width'), + '#title_display' => 'invisible', '#default_value' => $max_resolution[0], '#size' => 5, '#maxlength' => 5, @@ -93,6 +95,8 @@ function image_field_instance_settings_form($field, $instance) { ); $form['max_resolution']['y'] = array( '#type' => 'textfield', + '#title' => t('Maximum height'), + '#title_display' => 'invisible', '#default_value' => $max_resolution[1], '#size' => 5, '#maxlength' => 5, @@ -111,6 +115,8 @@ function image_field_instance_settings_form($field, $instance) { ); $form['min_resolution']['x'] = array( '#type' => 'textfield', + '#title' => t('Minimum width'), + '#title_display' => 'invisible', '#default_value' => $min_resolution[0], '#size' => 5, '#maxlength' => 5, @@ -118,6 +124,8 @@ function image_field_instance_settings_form($field, $instance) { ); $form['min_resolution']['y'] = array( '#type' => 'textfield', + '#title' => t('Minimum height'), + '#title_display' => 'invisible', '#default_value' => $min_resolution[1], '#size' => 5, '#maxlength' => 5, @@ -270,6 +278,7 @@ function image_field_widget_settings_form($field, $instance) { '#title' => t('Preview image style'), '#type' => 'select', '#options' => image_style_options(FALSE), + '#empty_option' => '<' . t('no preview') . '>', '#default_value' => $settings['preview_image_style'], '#description' => t('The preview image will be shown while editing the content.'), '#weight' => 15, @@ -419,7 +428,7 @@ function image_field_formatter_settings_form($field, $instance, $view_mode, $for $settings = $display['settings']; $image_styles = image_style_options(FALSE); - $form['image_style'] = array( + $element['image_style'] = array( '#title' => t('Image style'), '#type' => 'select', '#default_value' => $settings['image_style'], @@ -431,14 +440,15 @@ function image_field_formatter_settings_form($field, $instance, $view_mode, $for 'content' => t('Content'), 'file' => t('File'), ); - $form['image_link'] = array( + $element['image_link'] = array( '#title' => t('Link image to'), '#type' => 'select', '#default_value' => $settings['image_link'], + '#empty_option' => t('Nothing'), '#options' => $link_types, ); - return $form; + return $element; } /** diff --git a/modules/image/image.info b/modules/image/image.info index 744e7b51dce143412951d057fa073af10e1b2c7b..16c5cb8c35d86532d42b41b86a0b2f01ddbb68e3 100644 --- a/modules/image/image.info +++ b/modules/image/image.info @@ -13,8 +13,8 @@ files[] = image.install files[] = image.test configure = admin/config/media/image-styles -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/image/tests/image_module_test.info b/modules/image/tests/image_module_test.info index 4e113ee256f7e8a3b30a6735e50d317617c6d455..748c21b377e481a4b8ab1fe6c62f5f4a5b0f0ce7 100644 --- a/modules/image/tests/image_module_test.info +++ b/modules/image/tests/image_module_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = image_module_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index 3ce0a7bf2e76ad30c413c99512571eafbeb5cace..6398b680c40fed22c93edd7f13687e4ce6c8c973 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: locale.admin.inc,v 1.19 2010/10/04 18:00:46 dries Exp $ +// $Id: locale.admin.inc,v 1.20 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -28,6 +28,8 @@ function locale_languages_overview_form() { } $form['weight'][$langcode] = array( '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $language->name)), + '#title_display' => 'invisible', '#default_value' => $language->weight, '#attributes' => array('class' => array('language-order-weight')), ); @@ -35,11 +37,17 @@ function locale_languages_overview_form() { $form['native'][$langcode] = array('#markup' => check_plain($language->native)); $form['direction'][$langcode] = array('#markup' => ($language->direction == LANGUAGE_RTL ? t('Right to left') : t('Left to right'))); } - $form['enabled'] = array('#type' => 'checkboxes', + $form['enabled'] = array( + '#type' => 'checkboxes', + '#title' => t('Enabled languages'), + '#title_display' => 'invisible', '#options' => $options, '#default_value' => $enabled, ); - $form['site_default'] = array('#type' => 'radios', + $form['site_default'] = array( + '#type' => 'radios', + '#title' => t('Default language'), + '#title_display' => 'invisible', '#options' => $options, '#default_value' => language_default('language'), ); @@ -545,13 +553,20 @@ function _locale_languages_configure_form_language_table(&$form, $type) { $table_form['weight'][$id] = array( '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $provider['name'])), + '#title_display' => 'invisible', '#default_value' => $weight, '#attributes' => array('class' => array("language-provider-weight-$type")), ); $table_form['title'][$id] = array('#markup' => check_plain($provider['name'])); - $table_form['enabled'][$id] = array('#type' => 'checkbox', '#default_value' => $enabled); + $table_form['enabled'][$id] = array( + '#type' => 'checkbox', + '#title' => t('@title language provider', array('@title' => $provider['name'])), + '#title_display' => 'invisible', + '#default_value' => $enabled, + ); if ($id === LANGUAGE_NEGOTIATION_DEFAULT) { $table_form['enabled'][$id]['#default_value'] = TRUE; $table_form['enabled'][$id]['#attributes'] = array('disabled' => 'disabled'); diff --git a/modules/locale/locale.css b/modules/locale/locale.css index ea5676a0b6f563d0d52204547effa03c7bf03c5b..0c35d7aed9de550191bc53a6d947f6c9edf71489 100644 --- a/modules/locale/locale.css +++ b/modules/locale/locale.css @@ -1,4 +1,4 @@ -/* $Id: locale.css,v 1.7 2010/04/28 20:08:38 dries Exp $ */ +/* $Id: locale.css,v 1.8 2010/10/08 03:30:40 webchick Exp $ */ .locale-untranslated { font-style: normal; @@ -26,10 +26,10 @@ padding: 3ex 0 0 1em; } -.language-switcher-locale-session .active a.active { +.language-switcher-locale-session a.active { color: #0062A0; } -.language-switcher-locale-session .active a.session-active { +.language-switcher-locale-session a.session-active { color: #000000; } diff --git a/modules/locale/locale.info b/modules/locale/locale.info index 3b81e864795a798563449c89695b210d25d185c9..6556d6f35e19ec612e5255a93206f6c37f4b65dc 100644 --- a/modules/locale/locale.info +++ b/modules/locale/locale.info @@ -10,8 +10,8 @@ files[] = locale.admin.inc files[] = locale.test configure = admin/config/regional/language -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/locale/locale.install b/modules/locale/locale.install index 2fc1e46c5e827e3c0d4d57bc6898c9724bd09884..a9dc2306354af0124c63d3ced11a8348b18d4bf8 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -1,5 +1,5 @@ <?php -// $Id: locale.install,v 1.68 2010/10/05 20:04:18 webchick Exp $ +// $Id: locale.install,v 1.69 2010/10/20 00:37:20 dries Exp $ /** * @file @@ -38,7 +38,19 @@ function locale_install() { function locale_update_7000() { db_drop_index('locales_source', 'source'); db_add_index('locales_source', 'source_context', array(array('source', 30), 'context')); - db_change_field('locales_source', 'location', 'location', array('type' => 'text', 'size' => 'big', 'not null' => FALSE)); + + // Also drop the 'textgroup_location' index added by the i18nstrings module + // of the i18n project, which prevents the below schema update from running. + if (db_index_exists('locales_source', 'textgroup_location')) { + db_drop_index('locales_source', 'textgroup_location'); + } + + db_change_field('locales_source', 'location', 'location', array( + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + 'description' => 'Drupal path in case of online discovered translations or file path in case of imported strings.', + )); } /** diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 34b30337325d6e0cc263c4c90eb4cce241daaff4..9ecf90f531a70812adcfe66a8a191c94ee6c60c2 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -1,5 +1,5 @@ <?php -// $Id: locale.module,v 1.302 2010/10/05 19:59:10 dries Exp $ +// $Id: locale.module,v 1.303 2010/10/09 17:38:41 webchick Exp $ /** * @file @@ -941,7 +941,11 @@ function locale_block_view($type) { function locale_url_outbound_alter(&$path, &$options, $original_path) { // Only modify internal URLs. if (!$options['external'] && drupal_multilingual()) { - static $callbacks; + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast['callbacks'] = &drupal_static(__FUNCTION__); + } + $callbacks = &$drupal_static_fast['callbacks']; if (!isset($callbacks)) { $callbacks = array(); @@ -969,6 +973,11 @@ function locale_url_outbound_alter(&$path, &$options, $original_path) { foreach ($callbacks as $callback) { $callback($path, $options); } + + // No language dependent path allowed in this mode. + if (empty($callbacks)) { + unset($options['language']); + } } } diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 31bd5f5496dc3ec8f9d956d19b63b0c357b0bb47..7f716ba7fb806c6e36f454fdf66604540dcde7a0 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -1,5 +1,5 @@ <?php -// $Id: locale.test,v 1.80 2010/10/05 17:57:09 webchick Exp $ +// $Id: locale.test,v 1.81 2010/10/09 13:46:08 dries Exp $ /** * @file @@ -17,6 +17,7 @@ * - a functional test for configuring a different path alias per language; * - a functional test for multilingual support by content type and on nodes. * - a functional test for multilingual fields. + * - a functional test for comment language. */ @@ -1930,6 +1931,95 @@ class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase { } } +/** + * Functional tests for comment language. + */ +class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'Comment language', + 'description' => 'Tests for comment language.', + 'group' => 'Locale', + ); + } + + function setUp() { + parent::setUp('locale', 'locale_test'); + + // Create and login user. + $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'administer content types', 'create article content')); + $this->drupalLogin($admin_user); + + // Add language. + $edit = array('langcode' => 'fr'); + $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + + // Set "Article" content type to use multilingual support. + $edit = array('language_content_type' => 1); + $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type')); + + // Enable content language negotiation UI. + variable_set('locale_test_content_language_type', TRUE); + + // Set interface language detection to user and content language detection + // to URL. + $edit = array( + 'language[enabled][locale-user]' => TRUE, + 'language_content[enabled][locale-url]' => TRUE + ); + $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); + + // Change user language preference, this way interface language is always + // French no matter what path prefix the URLs have. + $edit = array('language' => 'fr'); + $this->drupalPost("user/{$admin_user->uid}/edit", $edit, t('Save')); + } + + /** + * Test that comment language is properly set. + */ + function testCommentLanguage() { + drupal_static_reset('language_list'); + + // Create two nodes, one for english and one for french, and comment each + // node using both english and french as content language by changing URL + // language prefixes. Meanwhile interface language is always French, which + // is the user language preference. This way we can ensure that node + // language and interface language do not influence comment language, as + // only content language has to. + foreach (language_list() as $node_langcode => $node_language) { + $language_none = LANGUAGE_NONE; + + // Create "Article" content. + $title = $this->randomName(); + $edit = array( + "title" => $title, + "body[$language_none][0][value]" => $this->randomName(), + "language" => $node_langcode, + ); + $this->drupalPost("node/add/article", $edit, t('Save')); + $node = $this->drupalGetNodeByTitle($title); + + foreach (language_list() as $langcode => $language) { + // Post a comment with content language $langcode. + $prefix = empty($language->prefix) ? '' : $language->prefix . '/'; + $edit = array("comment_body[$language_none][0][value]" => $this->randomName()); + $this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Save')); + + // Check that comment language matches the current content language. + $comment = db_select('comment', 'c') + ->fields('c') + ->condition('nid', $node->nid) + ->orderBy('cid', 'DESC') + ->execute() + ->fetchObject(); + $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->language, '%langcode' => $langcode); + $this->assertEqual($comment->language, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args)); + } + } + } +} /** * Functional tests for localizing date formats. */ diff --git a/modules/locale/tests/locale_test.info b/modules/locale/tests/locale_test.info index 3cdc2693d06ba9f96c7388329492fe0b88fedbe3..1acc5b89c3803a448c17981faa4978a8d7286951 100644 --- a/modules/locale/tests/locale_test.info +++ b/modules/locale/tests/locale_test.info @@ -7,8 +7,8 @@ files[] = locale_test.module version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/locale/tests/locale_test.module b/modules/locale/tests/locale_test.module index a6809b19adf53d5cd59ee603b9fb425f6ed7d8fd..e331b29d6628615797278d83c83c2ea5238102ad 100644 --- a/modules/locale/tests/locale_test.module +++ b/modules/locale/tests/locale_test.module @@ -1,5 +1,5 @@ <?php -// $Id: locale_test.module,v 1.5 2009/12/04 16:49:46 dries Exp $ +// $Id: locale_test.module,v 1.6 2010/10/09 13:46:09 dries Exp $ /** * @file @@ -17,6 +17,8 @@ function locale_test_locale($op = 'groups') { } /** + * Implements hook_boot(). + * * For testing domain language negotiation, we fake it by setting * the HTTP_HOST here */ @@ -25,3 +27,15 @@ function locale_test_boot() { $_SERVER['HTTP_HOST'] = variable_get('locale_test_domain'); } } + +/** + * Implements hook_language_types_info_alter(). + */ +function locale_test_language_types_info_alter(array &$language_types) { + if (variable_get('locale_test_content_language_type', FALSE)) { + $language_types[LANGUAGE_TYPE_CONTENT] = array( + 'name' => t('Content'), + 'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'), + ); + } +} diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc index f93293b7170db270a05ede44a9f1e20a866be5c7..06b3eb3af8aa813a15163c82a6435f95173cd8be 100644 --- a/modules/menu/menu.admin.inc +++ b/modules/menu/menu.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: menu.admin.inc,v 1.87 2010/10/04 18:00:46 dries Exp $ +// $Id: menu.admin.inc,v 1.90 2010/10/20 07:40:59 webchick Exp $ /** * @file @@ -103,6 +103,8 @@ function _menu_overview_tree_form($tree) { $form[$mlid]['title']['#markup'] = l($item['title'], $item['href'], $item['localized_options']) . ($item['hidden'] ? ' (' . t('disabled') . ')' : ''); $form[$mlid]['hidden'] = array( '#type' => 'checkbox', + '#title' => t('Enable @title menu link', array('@title' => $item['title'])), + '#title_display' => 'invisible', '#default_value' => !$item['hidden'], ); $form[$mlid]['weight'] = array( @@ -110,7 +112,7 @@ function _menu_overview_tree_form($tree) { '#delta' => 50, '#default_value' => $item['weight'], '#title_display' => 'invisible', - '#title' => t('Weight for @row', array('@row' => $item['title'])), + '#title' => t('Weight for @title', array('@title' => $item['title'])), ); $form[$mlid]['mlid'] = array( '#type' => 'hidden', @@ -419,57 +421,45 @@ function menu_edit_item_submit($form, &$form_state) { */ function menu_edit_menu($form, &$form_state, $type, $menu = array()) { $system_menus = menu_list_system_menus(); - $menu += array('menu_name' => '', 'old_name' => '', 'title' => '', 'description' => ''); - if (!empty($menu['menu_name'])) { - $menu['old_name'] = $menu['menu_name']; - } - $form['old_name'] = array('#type' => 'value', '#value' => $menu['old_name']); + $menu += array( + 'menu_name' => '', + 'old_name' => !empty($menu['menu_name']) ? $menu['menu_name'] : '', + 'title' => '', + 'description' => '', + ); + // Allow menu_edit_menu_submit() and other form submit handlers to determine + // whether the menu already exists. + $form['#insert'] = empty($menu['old_name']); + $form['old_name'] = array( + '#type' => 'value', + '#value' => $menu['old_name'], + ); - // The title of a system menu cannot be altered. - if (isset($system_menus[$menu['menu_name']])) { - $form['title'] = array('#type' => 'value', '#value' => $menu['title']); - } - else { - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Title'), - '#default_value' => $menu['title'], - '#required' => TRUE, - '#field_suffix' => ' <small id="edit-title-suffix"> </small>', - ); - } + $form['title'] = array( + '#type' => 'textfield', + '#title' => t('Title'), + '#default_value' => $menu['title'], + '#required' => TRUE, + // The title of a system menu cannot be altered. + '#access' => !isset($system_menus[$menu['menu_name']]), + ); - // The internal menu name can only be defined during initial menu creation. - if (!empty($menu['old_name'])) { - $form['#insert'] = FALSE; - $form['menu_name'] = array('#type' => 'value', '#value' => $menu['menu_name']); - } - else { - $form['#insert'] = TRUE; - $js_settings = array( - 'type' => 'setting', - 'data' => array( - 'machineReadableValue' => array( - 'title' => array( - 'text' => t('URL path'), - 'target' => 'menu-name', - 'searchPattern' => '[^a-z0-9]+', - 'replaceToken' => '-', - ), - ), - ), - ); - $form['menu_name'] = array( - '#type' => 'textfield', - '#title' => t('Menu name'), - '#maxsize' => MENU_MAX_MENU_NAME_LENGTH_UI, - '#description' => t('This text will be used to construct the URL for the menu. The name must contain only lowercase letters, numbers and hyphens, and must be unique.'), - '#required' => TRUE, - '#attached' => array( - 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings), - ), - ); - } + $form['menu_name'] = array( + '#type' => 'machine_name', + '#title' => t('Menu name'), + '#default_value' => $menu['menu_name'], + '#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI, + '#description' => t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'), + '#machine_name' => array( + 'exists' => 'menu_edit_menu_name_exists', + 'source' => array('title'), + 'label' => t('URL path'), + 'replace_pattern' => '[^a-z0-9-]+', + 'replace' => '-', + ), + // A menu's machine name cannot be changed. + '#disabled' => !empty($menu['old_name']) || isset($system_menus[$menu['menu_name']]), + ); $form['description'] = array( '#type' => 'textarea', @@ -560,26 +550,18 @@ function menu_delete_menu_confirm_submit($form, &$form_state) { } /** - * Validates the human and machine-readable names when adding or editing a menu. + * Returns whether a menu name already exists. + * + * @see menu_edit_menu() + * @see form_validate_machine_name() */ -function menu_edit_menu_validate($form, &$form_state) { - $item = $form_state['values']; - if (preg_match('/[^a-z0-9-]/', $item['menu_name'])) { - form_set_error('menu_name', t('The menu name may only consist of lowercase letters, numbers, and hyphens.')); - } - if ($form['#insert']) { - if (strlen($item['menu_name']) > MENU_MAX_MENU_NAME_LENGTH_UI) { - form_set_error('menu_name', format_plural(MENU_MAX_MENU_NAME_LENGTH_UI, "The menu name can't be longer than 1 character.", "The menu name can't be longer than @count characters.")); - } +function menu_edit_menu_name_exists($value) { + // 'menu-' is added to the menu name to avoid name-space conflicts. + $value = 'menu-' . $value; + $custom_exists = db_query_range('SELECT 1 FROM {menu_custom} WHERE menu_name = :menu', 0, 1, array(':menu' => $value))->fetchField(); + $link_exists = db_query_range("SELECT 1 FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $value))->fetchField(); - // We will add 'menu-' to the menu name to help avoid name-space conflicts. - $item['menu_name'] = 'menu-' . $item['menu_name']; - $custom_exists = db_query_range('SELECT 1 FROM {menu_custom} WHERE menu_name = :menu', 0, 1, array(':menu' => $item['menu_name']))->fetchField(); - $link_exists = db_query_range("SELECT 1 FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $item['menu_name']))->fetchField(); - if ($custom_exists || $link_exists) { - form_set_error('menu_name', t('The menu already exists.')); - } - } + return $custom_exists || $link_exists; } /** @@ -697,7 +679,7 @@ function menu_configure() { '#empty_option' => t('No Secondary links'), '#options' => $menu_options, '#tree' => FALSE, - '#description' => t("Select the source for the Secondary links. An advanced option allows you to use the same source for both Main links (currently %main) and Secondary links: if your source menu has two levels of hierarchy, the top level menu links will appear in the Main links, and the children of the active link will appear in the Secondary links." , array('%main' => $main_options[$main])), + '#description' => t('Select the source for the Secondary links. An advanced option allows you to use the same source for both Main links (currently %main) and Secondary links: if your source menu has two levels of hierarchy, the top level menu links will appear in the Main links, and the children of the active link will appear in the Secondary links.', array('%main' => $menu_options[$main])), ); return system_settings_form($form); diff --git a/modules/menu/menu.info b/modules/menu/menu.info index 11855f6f53fc8eb606c6abbcb778f4ead7ecde12..172857dd280276ceaed962c0bcda275d5ea10f7f 100644 --- a/modules/menu/menu.info +++ b/modules/menu/menu.info @@ -10,8 +10,8 @@ files[] = menu.install files[] = menu.test configure = admin/structure/menu -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/menu/menu.test b/modules/menu/menu.test index 22a527781bbd748fa0c4997c69bb2b73f0795847..a2c4ebdc3fa4d5da803b454b44d818ced43b2faf 100644 --- a/modules/menu/menu.test +++ b/modules/menu/menu.test @@ -1,5 +1,5 @@ <?php -// $Id: menu.test,v 1.41 2010/09/24 00:37:43 dries Exp $ +// $Id: menu.test,v 1.42 2010/10/13 13:43:21 dries Exp $ /** * @file @@ -140,7 +140,11 @@ class MenuTestCase extends DrupalWebTestCase { $this->drupalPost('admin/structure/menu/add', $edit, t('Save')); // Verify that using a menu_name that is too long results in a validation message. - $this->assertText(format_plural(MENU_MAX_MENU_NAME_LENGTH_UI, "The menu name can't be longer than 1 character.", "The menu name can't be longer than @count characters."), t('Validation failed when menu name is too long.')); + $this->assertRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array( + '!name' => t('Menu name'), + '%max' => MENU_MAX_MENU_NAME_LENGTH_UI, + '%length' => drupal_strlen($menu_name), + ))); // Change the menu_name so it no longer exceeds the maximum length. $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI); @@ -148,7 +152,11 @@ class MenuTestCase extends DrupalWebTestCase { $this->drupalPost('admin/structure/menu/add', $edit, t('Save')); // Verify that no validation error is given for menu_name length. - $this->assertNoText(format_plural(MENU_MAX_MENU_NAME_LENGTH_UI, "The menu name can't be longer than 1 character.", "The menu name can't be longer than @count characters."), t('Validation failed when menu name is too long.')); + $this->assertNoRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array( + '!name' => t('Menu name'), + '%max' => MENU_MAX_MENU_NAME_LENGTH_UI, + '%length' => drupal_strlen($menu_name), + ))); // Unlike most other modules, there is no confirmation message displayed. $this->drupalGet('admin/structure/menu'); diff --git a/modules/node/content_types.inc b/modules/node/content_types.inc index a9a70a1b3dbee070f8c46bd48b32ab22d2250a7a..67eacad7ace6b341f6d50a427024ffb44a6e019c 100644 --- a/modules/node/content_types.inc +++ b/modules/node/content_types.inc @@ -1,5 +1,5 @@ <?php -// $Id: content_types.inc,v 1.118 2010/10/07 00:28:20 webchick Exp $ +// $Id: content_types.inc,v 1.119 2010/10/13 13:43:21 dries Exp $ /** * @file @@ -94,41 +94,20 @@ function node_type_form($form, &$form_state, $type = NULL) { '#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the <em>Add new content</em> page. It is recommended that this name begin with a capital letter and contain only letters, numbers, and spaces. This name must be unique.'), '#required' => TRUE, '#size' => 30, - '#field_suffix' => ' <small id="edit-name-suffix">' . ($type->locked ? t('Machine name: @name', array('@name' => $type->type)) : ' ') . '</small>', ); - if (!$type->locked) { - $js_settings = array( - 'type' => 'setting', - 'data' => array( - 'machineReadableValue' => array( - 'name' => array( - 'text' => t('Machine name'), - 'target' => 'type', - 'searchPattern' => '[^a-z0-9]+', - 'replaceToken' => '_', - ), - ), - ), - ); - $form['type'] = array( - '#title' => t('Machine name'), - '#type' => 'textfield', - '#default_value' => $type->type, - '#maxlength' => 32, - '#required' => TRUE, - '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the <em>add new content</em> page for this content type. This name must contain only lowercase letters, numbers, and underscores. Underscores will be converted into hyphens when constructing the URL of the <em>add new content</em> page. This name must be unique.'), - '#attached' => array( - 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings), - ), - ); - } - else { - $form['type'] = array( - '#type' => 'value', - '#value' => $type->type, - ); - } + $form['type'] = array( + '#type' => 'machine_name', + '#default_value' => $type->type, + '#maxlength' => 32, + '#disabled' => $type->locked, + '#machine_name' => array( + 'exists' => 'node_type_load', + ), + '#description' => t('A unique machine-readable name for this content type. It must only contain lowercase letters, numbers, and underscores. This name will be used for constructing the URL of the %node-add page, in which underscores will be converted into hyphens.', array( + '%node-add' => t('Add new content'), + )), + ); $form['description'] = array( '#title' => t('Description'), @@ -276,12 +255,6 @@ function node_type_form_validate($form, &$form_state) { $types = node_type_get_names(); if (!$form_state['values']['locked']) { - if (isset($types[$type->type]) && $type->type != $old_type) { - form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => $type->type))); - } - if (!preg_match('!^[a-z0-9_]+$!', $type->type)) { - form_set_error('type', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.')); - } // 'theme' conflicts with theme_node_form(). // '0' is invalid, since elsewhere we check it using empty(). if (in_array($type->type, array('0', 'theme'))) { diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index 07c25512e6e5f4cc32aad6fdc1fc741af239f054..a1dfad52096c6a15ac0f5760712faafe2ef75ab9 100644 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: node.admin.inc,v 1.100 2010/10/06 13:38:40 dries Exp $ +// $Id: node.admin.inc,v 1.101 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -374,6 +374,8 @@ function node_admin_nodes() { } $form['options']['operation'] = array( '#type' => 'select', + '#title' => t('Operation'), + '#title_display' => 'invisible', '#options' => $options, '#default_value' => 'approve', ); diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 73c457bcc741233f141c4b6622f6c7b9bd71d7c2..980a748ea1f5cfa86ed72463f14367ae59e385b4 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -1,5 +1,5 @@ <?php -// $Id: node.api.php,v 1.77 2010/10/05 02:21:26 dries Exp $ +// $Id: node.api.php,v 1.78 2010/10/22 00:35:35 dries Exp $ /** * @file @@ -1017,8 +1017,16 @@ function hook_prepare($node) { * Display a node editing form. * * This hook, implemented by node modules, is called to retrieve the form - * that is displayed when one attempts to "create/edit" an item. This form is - * displayed at the URI http://www.example.com/?q=node/<add|edit>/nodetype. + * that is displayed to create or edit a node. This form is displayed at path + * node/add/[node type] or node/[node ID]/edit. + * + * The submit and preview buttons, administrative and display controls, and + * sections added by other modules (such as path settings, menu settings, + * comment settings, and fields managed by the Field UI module) are + * displayed automatically by the node module. This hook just needs to + * return the node title and form editing fields specific to the node type. + * + * For a detailed usage example, see node_example.module. * * @param $node * The node being added or edited. @@ -1026,21 +1034,21 @@ function hook_prepare($node) { * The form state array. * * @return - * An array containing the form elements to be displayed in the node - * edit form. - * - * The submit and preview buttons, taxonomy controls, and administrative - * accoutrements are displayed automatically by node.module. This hook - * needs to return the node title, the body text area, and fields - * specific to the node type. - * - * For a detailed usage example, see node_example.module. + * An array containing the title and any custom form elements to be displayed + * in the node editing form. * * @ingroup node_api_hooks */ function hook_form($node, &$form_state) { $type = node_type_get_type($node); + $form['title'] = array( + '#type' => 'textfield', + '#title' => check_plain($type->title_label), + '#default_value' => !empty($node->title) ? $node->title : '', + '#required' => TRUE, '#weight' => -5 + ); + $form['field1'] = array( '#type' => 'textfield', '#title' => t('Custom field'), diff --git a/modules/node/node.info b/modules/node/node.info index 4c3e8a5afa6bd06d564d8a4c17ae6752e5b1b16f..81d485de1407926f396757023931bd4eca1918a7 100644 --- a/modules/node/node.info +++ b/modules/node/node.info @@ -15,8 +15,8 @@ required = TRUE configure = admin/structure/types stylesheets[all][] = node.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/node/node.module b/modules/node/node.module index b12b91b4a522e603ba744346ad71162f5bbb6388..acf4d1d9a7ec548429bcf027f62c95eefdce9f12 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -1,5 +1,5 @@ <?php -// $Id: node.module,v 1.1305 2010/10/07 00:28:20 webchick Exp $ +// $Id: node.module,v 1.1309 2010/10/20 08:15:33 webchick Exp $ /** * @file @@ -1171,6 +1171,7 @@ function node_delete_multiple($nids) { // Call the node-specific callback (if any): node_invoke($node, 'delete'); module_invoke_all('node_delete', $node); + module_invoke_all('entity_delete', $node, 'node'); field_attach_delete('node', $node); // Remove this node from the search index if needed. @@ -1192,6 +1193,9 @@ function node_delete_multiple($nids) { db_delete('history') ->condition('nid', $nids, 'IN') ->execute(); + db_delete('node_access') + ->condition('nid', $nids, 'IN') + ->execute(); // Clear the page and block and node_load_multiple caches. cache_clear_all(); @@ -1889,14 +1893,15 @@ function node_menu() { $items['node'] = array( 'page callback' => 'node_page_default', 'access arguments' => array('access content'), + // Required to make 'node/add' appear on the top-level of 'navigation' menu. + 'menu_name' => '', 'type' => MENU_CALLBACK, ); $items['node/add'] = array( - 'title' => 'Add new content', + 'title' => 'Add content', 'page callback' => 'node_add_page', 'access callback' => '_node_add_access', - 'weight' => 1, - 'menu_name' => 'management', + 'menu_name' => 'navigation', 'theme callback' => '_node_custom_theme', 'file' => 'node.pages.inc', ); @@ -1968,6 +1973,7 @@ function node_menu() { 'page arguments' => array(1), 'access callback' => '_node_revision_access', 'access arguments' => array(1), + 'theme callback' => '_node_custom_theme', 'weight' => 2, 'type' => MENU_LOCAL_TASK, 'file' => 'node.pages.inc', @@ -1987,6 +1993,7 @@ function node_menu() { 'page arguments' => array('node_revision_revert_confirm', 1), 'access callback' => '_node_revision_access', 'access arguments' => array(1, 'update'), + 'theme callback' => '_node_custom_theme', 'file' => 'node.pages.inc', ); $items['node/%node/revisions/%/delete'] = array( @@ -1996,6 +2003,7 @@ function node_menu() { 'page arguments' => array('node_revision_delete_confirm', 1), 'access callback' => '_node_revision_access', 'access arguments' => array(1, 'delete'), + 'theme callback' => '_node_custom_theme', 'file' => 'node.pages.inc', ); return $items; diff --git a/modules/node/node.test b/modules/node/node.test index 7e5eff7546e80cbcddef3428690801b0f03eec38..b27ba4f0513989354396e3a507a57bbbd01ea051 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -1,5 +1,5 @@ <?php -// $Id: node.test,v 1.97 2010/10/07 00:28:20 webchick Exp $ +// $Id: node.test,v 1.98 2010/10/07 17:31:39 webchick Exp $ /** * Test the node_load_multiple() function. @@ -1278,6 +1278,30 @@ class NodeAdminTestCase extends DrupalWebTestCase { $this->base_user_3 = $this->drupalCreateUser(array('access content overview', 'bypass node access')); } + /** + * Tests that the table sorting works on the content admin pages. + */ + function testContentAdminSort() { + $this->drupalLogin($this->admin_user); + foreach (array('dd', 'aa', 'DD', 'bb', 'cc', 'CC', 'AA', 'BB') as $prefix) { + $this->drupalCreateNode(array('title' => $prefix . $this->randomName(6))); + } + + // Compare the rendered HTML node list to a query for the nodes ordered by + // title to account for possible database-dependent sort order. + $nodes_query = db_select('node', 'n') + ->fields('n', array('nid')) + ->orderBy('title') + ->execute() + ->fetchCol(); + + $this->drupalGet('admin/content', array('query' => array('sort' => 'asc', 'order' => 'Title'))); + foreach ($this->xpath('//table/tbody/tr/td/div/input/@value') as $input) { + $nodes_form[] = $input; + } + $this->assertEqual($nodes_query, $nodes_form, 'Nodes are sorted in the form the same as they are in the query.'); + } + /** * Tests content overview with different user permissions. * diff --git a/modules/node/tests/node_access_test.info b/modules/node/tests/node_access_test.info index 9f111a44db95ec0f05003073fa513fe0c717795c..645ffea1dd3183e1f7f8f3542cd61250f6d567de 100644 --- a/modules/node/tests/node_access_test.info +++ b/modules/node/tests/node_access_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = node_access_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/node/tests/node_presave_test.info b/modules/node/tests/node_presave_test.info index d63891915b4e4d1d0b5a42cf7b975229f2e17e3d..67d4075419bf549cf9569e113edae3b14b444b22 100644 --- a/modules/node/tests/node_presave_test.info +++ b/modules/node/tests/node_presave_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = node_presave_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/node/tests/node_test.info b/modules/node/tests/node_test.info index 097e84e864023bac51fcc52fa7e09df9b8fde7e6..e5c7db00f7814a52a63948087ba32d2d2e4bb163 100644 --- a/modules/node/tests/node_test.info +++ b/modules/node/tests/node_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = node_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/node/tests/node_test_exception.info b/modules/node/tests/node_test_exception.info index 8933ae58ddad2ecb2a2e93506f6bf371be9c18f1..67ce0bce261c19741d242f77df96c08e88c3fbe0 100644 --- a/modules/node/tests/node_test_exception.info +++ b/modules/node/tests/node_test_exception.info @@ -7,8 +7,8 @@ core = 7.x files[] = node_test_exception.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/openid/openid.info b/modules/openid/openid.info index 50a153cad3e37fd4eb0a73ba883559bbb3f14fed..d091b8f4b53896ffc14d89b08b8a24d2068c146d 100644 --- a/modules/openid/openid.info +++ b/modules/openid/openid.info @@ -10,8 +10,8 @@ files[] = openid.pages.inc files[] = openid.install files[] = openid.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/openid/openid.test b/modules/openid/openid.test index c695ce62c4fcffeb18665df5c2d195eeb60da224..b0f88bbbfe8b81bbb791ac7e9ff1d012bf8e6530 100644 --- a/modules/openid/openid.test +++ b/modules/openid/openid.test @@ -1,5 +1,5 @@ <?php -// $Id: openid.test,v 1.31 2010/08/22 22:00:16 dries Exp $ +// $Id: openid.test,v 1.32 2010/10/08 05:28:30 webchick Exp $ /** * Base class for OpenID tests. @@ -20,6 +20,16 @@ abstract class OpenIDWebTestCase extends DrupalWebTestCase { // Submit form to the OpenID Provider Endpoint. $this->drupalPost(NULL, array(), t('Send')); } + + /** + * Parses the last sent e-mail and returns the one-time login link URL. + */ + function getPasswordResetURLFromMail() { + $mails = $this->drupalGetMails(); + $mail = end($mails); + preg_match('@.+user/reset/.+@', $mail['body'], $matches); + return $matches[0]; + } } /** @@ -126,7 +136,7 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { // Test logging in via the login block on the front page. $this->submitLoginForm($identity); - $this->assertLink($this->web_user->name, 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); $this->drupalLogout(); @@ -140,7 +150,7 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { // Submit form to the OpenID Provider Endpoint. $this->drupalPost(NULL, array(), t('Send')); - $this->assertLink($this->web_user->name, 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); // Verify user was redirected away from user/login to an accessible page. $this->assertResponse(200); @@ -171,7 +181,7 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { // Submit form to the OpenID Provider Endpoint. $this->drupalPost(NULL, array(), t('Send')); - $this->assertLink($this->web_user->name, 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); // Verify user was redirected away from user/login to an accessible page. $this->assertText(t('Operating in maintenance mode.')); @@ -316,6 +326,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $this->submitLoginForm($identity); $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), t('User was asked to verify e-mail address.')); $this->assertRaw(t('A welcome message with further instructions has been sent to your e-mail address.'), t('A welcome message was sent to the user.')); + $reset_url = $this->getPasswordResetURLFromMail(); $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); @@ -326,14 +337,14 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $this->assertRaw(t('You must validate your email address for this account before logging in via OpenID.')); // Follow the one-time login that was sent in the welcome e-mail. - $this->drupalGet(user_pass_reset_url($user)); + $this->drupalGet($reset_url); $this->drupalPost(NULL, array(), t('Log in')); $this->drupalLogout(); // Verify that the account was activated. $this->submitLoginForm($identity); - $this->assertLink('john', 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); } /** @@ -348,7 +359,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { // Use a User-supplied Identity that is the URL of an XRDS document. $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE)); $this->submitLoginForm($identity); - $this->assertLink('john', 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); @@ -358,7 +369,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $this->drupalLogout(); $this->submitLoginForm($identity); - $this->assertLink('john', 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); } /** @@ -382,13 +393,14 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $edit = array('name' => 'john', 'mail' => 'john@example.com'); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), t('User was asked to verify e-mail address.')); + $reset_url = $this->getPasswordResetURLFromMail(); $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); $this->assertFalse($user->data, t('No additional user info was saved.')); // Follow the one-time login that was sent in the welcome e-mail. - $this->drupalGet(user_pass_reset_url($user)); + $this->drupalGet($reset_url); $this->drupalPost(NULL, array(), t('Log in')); // The user is taken to user/%uid/edit. @@ -417,13 +429,14 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { $edit = array('name' => 'john', 'mail' => 'john@example.com'); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertRaw(t('Once you have verified your e-mail address, you may log in via OpenID.'), t('User was asked to verify e-mail address.')); + $reset_url = $this->getPasswordResetURLFromMail(); $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); $this->assertFalse($user->data, t('No additional user info was saved.')); // Follow the one-time login that was sent in the welcome e-mail. - $this->drupalGet(user_pass_reset_url($user)); + $this->drupalGet($reset_url); $this->drupalPost(NULL, array(), t('Log in')); // The user is taken to user/%uid/edit. @@ -453,7 +466,7 @@ class OpenIDRegistrationTestCase extends OpenIDWebTestCase { // Use a User-supplied Identity that is the URL of an XRDS document. $identity = url('openid-test/yadis/xrds', array('absolute' => TRUE)); $this->submitLoginForm($identity); - $this->assertLink('john', 0, t('User was logged in.')); + $this->assertLink(t('Log out'), 0, t('User was logged in.')); $user = user_load_by_name('john'); $this->assertTrue($user, t('User was registered with right username.')); diff --git a/modules/openid/tests/openid_test.info b/modules/openid/tests/openid_test.info index 73b2cd491f96fc5ffcce0e6a45ab5b7902ae0639..d16228bdf99933f18d72953f3392532aff664487 100644 --- a/modules/openid/tests/openid_test.info +++ b/modules/openid/tests/openid_test.info @@ -9,8 +9,8 @@ files[] = openid_test.module dependencies[] = openid hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/overlay/overlay.info b/modules/overlay/overlay.info index 3d105f86e8a5f17dafd3e26479dc06bf44f03f48..bb6d28aeee0b9e8d400550f3bb427f94c5307acf 100644 --- a/modules/overlay/overlay.info +++ b/modules/overlay/overlay.info @@ -7,8 +7,8 @@ core = 7.x files[] = overlay.module files[] = overlay.install -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/path/path.info b/modules/path/path.info index c3cbc783f3eeee6e68e7d12710ba35f0f5930037..95507c9343eda1e208530a09273294c642ef2c26 100644 --- a/modules/path/path.info +++ b/modules/path/path.info @@ -9,8 +9,8 @@ files[] = path.admin.inc files[] = path.test configure = admin/config/search/path -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/path/path.test b/modules/path/path.test index 4b524d5248bbd83a05eea1078ae172474e34d90c..9666466b05779e056888a21d9b56a293da2af03a 100644 --- a/modules/path/path.test +++ b/modules/path/path.test @@ -1,5 +1,5 @@ <?php -// $Id: path.test,v 1.40 2010/08/05 23:53:38 webchick Exp $ +// $Id: path.test,v 1.41 2010/10/09 17:38:41 webchick Exp $ /** * @file @@ -299,6 +299,7 @@ class PathLanguageTestCase extends DrupalWebTestCase { // Confirm that the alias is returned by url(). drupal_static_reset('language_list'); + drupal_static_reset('locale_url_outbound_alter'); $languages = language_list(); $url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language])); $this->assertTrue(strpos($url, $edit['path[alias]']), t('URL contains the path alias.')); diff --git a/modules/php/php.info b/modules/php/php.info index 282d58c2339005000b1b340dc9d71eeac3bdcce4..8a6983ac83f301222969ec1320ed31b9a982faaa 100644 --- a/modules/php/php.info +++ b/modules/php/php.info @@ -8,8 +8,8 @@ files[] = php.module files[] = php.install files[] = php.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/php/php.install b/modules/php/php.install index bc5d7d66d28f36ee4d0eae4bdfcbd79e4b7ad535..b5a4853dcbf728aed1df3b83844a32c49731a496 100644 --- a/modules/php/php.install +++ b/modules/php/php.install @@ -1,5 +1,5 @@ <?php -// $Id: php.install,v 1.18 2010/09/13 01:11:08 dries Exp $ +// $Id: php.install,v 1.19 2010/10/20 01:15:58 dries Exp $ /** * @file @@ -17,6 +17,7 @@ function php_enable() { // subsequent clean installs. if (!$format_exists) { $php_format = array( + 'format' => 'php_code', 'name' => 'PHP code', // 'Plain text' format is installed with a weight of 10 by default. Use a // higher weight here to ensure that this format will not be the default diff --git a/modules/poll/poll.info b/modules/poll/poll.info index 5dba7d7757764bcff25f31db8bc3e3729c05b306..26caaa01cce93e667a065a4f680410ce62855467 100644 --- a/modules/poll/poll.info +++ b/modules/poll/poll.info @@ -11,8 +11,8 @@ files[] = poll.test files[] = poll.tokens.inc stylesheets[all][] = poll.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/poll/poll.module b/modules/poll/poll.module index f65c20296c289910bd09da2f255af19c9323f0fb..9b30fa296beac43ee464777aada0b553d50c9871 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -1,5 +1,5 @@ <?php -// $Id: poll.module,v 1.357 2010/09/24 02:05:55 dries Exp $ +// $Id: poll.module,v 1.358 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -390,12 +390,16 @@ function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight $form['chtext'] = array( '#type' => 'textfield', + '#title' => $value !== '' ? t('Choice label') : t('New choice label'), + '#title_display' => 'invisible', '#default_value' => $value, '#parents' => array('choice', $key, 'chtext'), ); $form['chvotes'] = array( '#type' => 'textfield', + '#title' => $value !== '' ? t('Vote count for choice @label', array('@label' => $value)) : t('Vote count for new choice'), + '#title_display' => 'invisible', '#default_value' => $votes, '#size' => 5, '#maxlength' => 7, @@ -405,6 +409,8 @@ function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight $form['weight'] = array( '#type' => 'weight', + '#title' => $value !== '' ? t('Weight for choice @label', array('@label' => $value)) : t('Weight for new choice'), + '#title_display' => 'invisible', '#default_value' => $weight, '#delta' => $size, '#parents' => array('choice', $key, 'weight'), @@ -688,6 +694,8 @@ function poll_view_voting($form, &$form_state, $node, $block = FALSE) { } $form['choice'] = array( '#type' => 'radios', + '#title' => t('Choices'), + '#title_display' => 'invisible', '#default_value' => -1, '#options' => $list, ); diff --git a/modules/profile/profile.admin.inc b/modules/profile/profile.admin.inc index 8cf48a662a6cac6f761957115b8252bfddfac19f..6c1c5e4dcd971fee17ff983e6ed32aac64ba9bca 100644 --- a/modules/profile/profile.admin.inc +++ b/modules/profile/profile.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: profile.admin.inc,v 1.42 2010/04/24 14:49:14 dries Exp $ +// $Id: profile.admin.inc,v 1.43 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -24,8 +24,19 @@ function profile_admin_overview($form) { $form[$field->fid]['name'] = array('#markup' => check_plain($field->name)); $form[$field->fid]['title'] = array('#markup' => check_plain($field->title)); $form[$field->fid]['type'] = array('#markup' => $field->type); - $form[$field->fid]['category'] = array('#type' => 'select', '#default_value' => $field->category, '#options' => array()); - $form[$field->fid]['weight'] = array('#type' => 'weight', '#default_value' => $field->weight); + $form[$field->fid]['category'] = array( + '#type' => 'select', + '#title' => t('Category for @title', array('@title' => $field->title)), + '#title_display' => 'invisible', + '#default_value' => $field->category, + '#options' => array(), + ); + $form[$field->fid]['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $field->title)), + '#title_display' => 'invisible', + '#default_value' => $field->weight, + ); $form[$field->fid]['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => "admin/config/people/profile/edit/$field->fid"); $form[$field->fid]['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => "admin/config/people/profile/delete/$field->fid"); } diff --git a/modules/profile/profile.info b/modules/profile/profile.info index ac348755adecd3e3ce0ac759c28f8ab1874a79ce..151a64dad9c66dd2dc02b9b43c93a7c87a7fd990 100644 --- a/modules/profile/profile.info +++ b/modules/profile/profile.info @@ -11,8 +11,8 @@ files[] = profile.install files[] = profile.test configure = admin/config/people/profile -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/rdf/rdf.info b/modules/rdf/rdf.info index a74cb4030a4a2e2d0346cda4a23924fa92745416..00ed1e429efda0a3064939fe6b134cf22e761f2c 100644 --- a/modules/rdf/rdf.info +++ b/modules/rdf/rdf.info @@ -8,8 +8,8 @@ files[] = rdf.install files[] = rdf.module files[] = rdf.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/rdf/tests/rdf_test.info b/modules/rdf/tests/rdf_test.info index 5f23bd1fe1ba1b91e46e1aa7e1071ae48ee4faa5..d1714652509c17e2f95ad335e5dda47dcd97d3b3 100644 --- a/modules/rdf/tests/rdf_test.info +++ b/modules/rdf/tests/rdf_test.info @@ -8,8 +8,8 @@ files[] = rdf_test.install files[] = rdf_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/search/search.admin.inc b/modules/search/search.admin.inc index bde2d0f03d5879f9d0018ac3ef5a6cda6747c088..d3c668fb535a22108e1939acc8fda12c7048e8fe 100644 --- a/modules/search/search.admin.inc +++ b/modules/search/search.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: search.admin.inc,v 1.18 2010/09/28 02:30:31 dries Exp $ +// $Id: search.admin.inc,v 1.19 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -117,6 +117,8 @@ function search_admin_settings($form) { ); $form['active']['search_active_modules'] = array( '#type' => 'checkboxes', + '#title' => t('Active modules'), + '#title_display' => 'invisible', '#default_value' => variable_get('search_active_modules', array('node', 'user')), '#options' => _search_get_module_names(), '#description' => t('Choose which search modules are active from the available modules.') diff --git a/modules/search/search.info b/modules/search/search.info index 7da514b0881794f1ca338a0b81839f2491f2723f..d99e2d9afc10f87ea46cf1f6fa599d15361d4bbd 100644 --- a/modules/search/search.info +++ b/modules/search/search.info @@ -13,8 +13,8 @@ files[] = search.extender.inc configure = admin/config/search/settings stylesheets[all][] = search.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/search/search.module b/modules/search/search.module index a73da3c9e1e46ebc174dc77fb213691b9c309d4e..9923dd2fdb4ed487e86f9844161e6e0fb56cf016 100644 --- a/modules/search/search.module +++ b/modules/search/search.module @@ -1,5 +1,5 @@ <?php -// $Id: search.module,v 1.363 2010/10/01 15:24:18 webchick Exp $ +// $Id: search.module,v 1.365 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -978,7 +978,7 @@ function search_form($form, &$form_state, $action = '', $keys = '', $type = NULL ); // processed_keys is used to coordinate keyword passing between other forms // that hook into the basic search form. - $form['basic']['processed_keys'] = array('#type' => 'value', '#value' => array()); + $form['basic']['processed_keys'] = array('#type' => 'value', '#value' => ''); $form['basic']['submit'] = array('#type' => 'submit', '#value' => t('Search')); return $form; @@ -995,6 +995,8 @@ function search_form($form, &$form_state, $action = '', $keys = '', $type = NULL function search_box($form, &$form_state, $form_id) { $form[$form_id] = array( '#type' => 'textfield', + '#title' => t('Search'), + '#title_display' => 'invisible', '#size' => 15, '#default_value' => '', '#attributes' => array('title' => t('Enter the terms you wish to search for.')), diff --git a/modules/search/search.test b/modules/search/search.test index 18796617cf31d236c7e7c1071a36b6f994947ba1..986db177e75da862cadc9f736e1edad794f10e88 100644 --- a/modules/search/search.test +++ b/modules/search/search.test @@ -1,5 +1,5 @@ <?php -// $Id: search.test,v 1.76 2010/10/05 06:17:29 webchick Exp $ +// $Id: search.test,v 1.77 2010/10/20 01:15:58 dries Exp $ // The search index can contain different types of content. Typically the type is 'node'. // Here we test with _test_ and _test2_ as the type. @@ -370,7 +370,11 @@ class SearchRankingTestCase extends DrupalWebTestCase { // Create nodes for testing. foreach ($node_ranks as $node_rank) { - $settings = array('type' => 'page', 'title' => array(LANGUAGE_NONE => array(array('value' => 'Drupal rocks'))), 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks")))); + $settings = array( + 'type' => 'page', + 'title' => 'Drupal rocks', + 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), + ); foreach (array(0, 1) as $num) { if ($num == 1) { switch ($node_rank) { @@ -444,18 +448,18 @@ class SearchRankingTestCase extends DrupalWebTestCase { shuffle($shuffled_tags); $settings = array( 'type' => 'page', - 'title' => array(LANGUAGE_NONE => array(array('value' => 'Simple node'))), + 'title' => 'Simple node', ); foreach ($shuffled_tags as $tag) { switch ($tag) { case 'a': - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 3))); + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 'full_html'))); break; case 'notag': $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'Drupal Rocks'))); break; default: - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 3))); + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html'))); break; } $nodes[$tag] = $this->drupalCreateNode($settings); @@ -488,7 +492,7 @@ class SearchRankingTestCase extends DrupalWebTestCase { // Test tags with the same weight against the sorted tags. $unsorted_tags = array('u', 'b', 'i', 'strong', 'em'); foreach ($unsorted_tags as $tag) { - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 3))); + $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks</$tag>", 'format' => 'full_html'))); $node = $this->drupalCreateNode($settings); // Update the search index. @@ -523,7 +527,7 @@ class SearchRankingTestCase extends DrupalWebTestCase { // See testRankings() above - build a node that will rank high for sticky. $settings = array( 'type' => 'page', - 'title' => array(LANGUAGE_NONE => array(array('value' => 'Drupal rocks'))), + 'title' => 'Drupal rocks', 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), 'sticky' => 1, ); @@ -712,9 +716,9 @@ class SearchCommentTestCase extends DrupalWebTestCase { // Enable check_plain() for 'Filtered HTML' text format. $filtered_html_format_id = db_query_range('SELECT format FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'Filtered HTML'))->fetchField(); $edit = array( - 'filters[filter_html_escape][status]' => $filtered_html_format_id, + 'filters[filter_html_escape][status]' => TRUE, ); - $this->drupalPost('admin/config/content/formats/1', $edit, t('Save configuration')); + $this->drupalPost('admin/config/content/formats/' . $filtered_html_format_id, $edit, t('Save configuration')); // Allow anonymous users to search content. $edit = array( DRUPAL_ANONYMOUS_RID . '[search content]' => 1, diff --git a/modules/search/tests/search_embedded_form.info b/modules/search/tests/search_embedded_form.info index 8c256467f45d70d2e39776446663ef2af1424df9..2c24356e63b25858a57060f20df5738e4d8fcbaa 100644 --- a/modules/search/tests/search_embedded_form.info +++ b/modules/search/tests/search_embedded_form.info @@ -7,8 +7,8 @@ core = 7.x files[] = search_embedded_form.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/search/tests/search_extra_type.info b/modules/search/tests/search_extra_type.info index 35b300674e958b03eaedbac1d7924e2088fe7ee0..9d2930f5530bd722bb80c594c0d98ac6b349abb0 100644 --- a/modules/search/tests/search_extra_type.info +++ b/modules/search/tests/search_extra_type.info @@ -7,8 +7,8 @@ core = 7.x files[] = search_extra_type.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/shortcut/shortcut.admin.inc b/modules/shortcut/shortcut.admin.inc index d8eadca6c44bf878fe061746dc4e6d04fb636060..4c68a333cb75486bb3f3151c137105a8f6252043 100644 --- a/modules/shortcut/shortcut.admin.inc +++ b/modules/shortcut/shortcut.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: shortcut.admin.inc,v 1.14 2010/06/30 15:12:59 dries Exp $ +// $Id: shortcut.admin.inc,v 1.16 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -72,6 +72,8 @@ function shortcut_set_switch($form, &$form_state, $account = NULL) { $form['new'] = array( '#type' => 'textfield', + '#title' => t('Name'), + '#title_display' => 'invisible', '#description' => t('The new set is created by copying items from your default shortcut set.'), '#access' => $add_access, ); @@ -459,6 +461,9 @@ function _shortcut_link_form_elements($shortcut_link = NULL) { 'link_path' => '' ); } + else { + $shortcut_link['link_path'] = drupal_get_path_alias($shortcut_link['link_path']); + } $form['shortcut_link']['#tree'] = TRUE; $form['shortcut_link']['link_title'] = array( @@ -504,7 +509,11 @@ function shortcut_link_edit_validate($form, &$form_state) { * Submit handler for shortcut_link_edit(). */ function shortcut_link_edit_submit($form, &$form_state) { + // Normalize the path in case it is an alias. + $form_state['values']['shortcut_link']['link_path'] = drupal_get_normal_path($form_state['values']['shortcut_link']['link_path']); + $shortcut_link = array_merge($form_state['values']['original_shortcut_link'], $form_state['values']['shortcut_link']); + menu_link_save($shortcut_link); $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; drupal_set_message(t('The shortcut %link has been updated.', array('%link' => $shortcut_link['link_title']))); @@ -556,6 +565,9 @@ function shortcut_admin_add_link($shortcut_link, &$shortcut_set, $limit = NULL) } } + // Normalize the path in case it is an alias. + $shortcut_link['link_path'] = drupal_get_normal_path($shortcut_link['link_path']); + // Add the link to the end of the list. $shortcut_set->links[] = $shortcut_link; shortcut_set_reset_link_weights($shortcut_set); diff --git a/modules/shortcut/shortcut.css b/modules/shortcut/shortcut.css index 51556ab21c799d370ed1ad6f3c3652e85698f23f..032544252683450d5b246f9c9b25310c98d1d839 100644 --- a/modules/shortcut/shortcut.css +++ b/modules/shortcut/shortcut.css @@ -1,14 +1,15 @@ -/* $Id: shortcut.css,v 1.8 2010/05/31 08:18:02 dries Exp $ */ +/* $Id: shortcut.css,v 1.9 2010/10/20 01:06:51 dries Exp $ */ div#toolbar a#edit-shortcuts { float: right; padding: 5px 10px 5px 5px; line-height: 24px; - color: #bbb; + color: #fefefe; } div#toolbar a#edit-shortcuts:focus, div#toolbar a#edit-shortcuts:hover, div#toolbar a#edit-shortcuts.active { color: #fff; + text-decoration: underline; } div#toolbar div.toolbar-shortcuts ul { diff --git a/modules/shortcut/shortcut.info b/modules/shortcut/shortcut.info index 84d5db36ef7b158cf87eb032b09e128d29e5625f..3b126c0ea321643f95d60c52326905d59b1ef0c8 100644 --- a/modules/shortcut/shortcut.info +++ b/modules/shortcut/shortcut.info @@ -10,8 +10,8 @@ files[] = shortcut.install files[] = shortcut.test configure = admin/config/user-interface/shortcut -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/shortcut/shortcut.test b/modules/shortcut/shortcut.test index a3fd392497a8026bb58b72abb8c110b5142d2bb5..bf02ea37655e761e541d1bee006cf9a240d344ba 100644 --- a/modules/shortcut/shortcut.test +++ b/modules/shortcut/shortcut.test @@ -1,5 +1,5 @@ <?php -// $Id: shortcut.test,v 1.5 2010/08/05 23:53:38 webchick Exp $ +// $Id: shortcut.test,v 1.6 2010/10/15 04:21:02 webchick Exp $ /** * @file @@ -36,8 +36,10 @@ class ShortcutTestCase extends DrupalWebTestCase { // Create users. $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'create article content', 'create page content', 'access content overview')); $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets')); + // Create a node. $this->node = $this->drupalCreateNode(array('type' => 'article')); + // Log in as admin and grab the default shortcut set. $this->drupalLogin($this->admin_user); $this->set = shortcut_set_load(SHORTCUT_DEFAULT_SET_NAME); @@ -114,10 +116,19 @@ class ShortcutLinksTestCase extends ShortcutTestCase { function testShortcutLinkAdd() { $set = $this->set; + // Create an alias for the node so we can test aliases. + $path = array( + 'source' => 'node/' . $this->node->nid, + 'alias' => $this->randomName(8), + ); + path_save($path); + + // Create some paths to test. $test_cases = array( array('path' => 'admin'), array('path' => 'admin/config/system/site-information'), array('path' => "node/{$this->node->nid}/edit"), + array('path' => $path['alias']), ); // Check that each new shortcut links where it should. @@ -131,7 +142,7 @@ class ShortcutLinksTestCase extends ShortcutTestCase { $this->assertResponse(200); $saved_set = shortcut_set_load($set->set_name); $paths = $this->getShortcutInformation($saved_set, 'link_path'); - $this->assertTrue(in_array($test['path'], $paths), 'Shortcut created: '. $test['path']); + $this->assertTrue(in_array(drupal_get_normal_path($test['path']), $paths), 'Shortcut created: '. $test['path']); $this->assertLink($title, 0, 'Shortcut link found on the page.'); } } diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 3355fc248a50ba813bfeb4cf1094352ee93639bb..76c0248d3565c0fdbe928ec913389f963f60bf79 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -1,5 +1,5 @@ <?php -// $Id: drupal_web_test_case.php,v 1.240 2010/10/05 06:17:29 webchick Exp $ +// $Id: drupal_web_test_case.php,v 1.243 2010/10/23 02:26:11 webchick Exp $ /** * Global variable that holds information about the tests being run. @@ -1801,7 +1801,7 @@ class DrupalWebTestCase extends DrupalTestCase { * * @see ajax.js */ - protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = 'system/ajax', array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) { + protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = NULL, array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) { // Get the content of the initial page prior to calling drupalPost(), since // drupalPost() replaces $this->content. if (isset($path)) { @@ -1838,6 +1838,12 @@ class DrupalWebTestCase extends DrupalTestCase { $extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id); } + // Unless a particular path is specified, use the one specified by the + // AJAX settings, or else 'system/ajax'. + if (!isset($ajax_path)) { + $ajax_path = isset($ajax_settings['url']) ? $ajax_settings['url'] : 'system/ajax'; + } + // Submit the POST request. $return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post)); @@ -2798,7 +2804,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field exists in the current page by the given XPath. + * Asserts that a field exists in the current page by the given XPath. * * @param $xpath * XPath used to find the field. @@ -2870,7 +2876,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field does not exist in the current page by the given XPath. + * Asserts that a field does not exist in the current page by the given XPath. * * @param $xpath * XPath used to find the field. @@ -2902,7 +2908,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field exists in the current page with the given name and value. + * Asserts that a field exists in the current page with the given name and value. * * @param $name * Name of field to assert. @@ -2920,7 +2926,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field does not exist with the given name and value. + * Asserts that a field does not exist with the given name and value. * * @param $name * Name of field to assert. @@ -2938,7 +2944,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field exists in the current page with the given id and value. + * Asserts that a field exists in the current page with the given id and value. * * @param $id * Id of field to assert. @@ -2956,7 +2962,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field does not exist with the given id and value. + * Asserts that a field does not exist with the given id and value. * * @param $id * Id of field to assert. @@ -2974,7 +2980,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a checkbox field in the current page is checked. + * Asserts that a checkbox field in the current page is checked. * * @param $id * Id of field to assert. @@ -2989,7 +2995,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a checkbox field in the current page is not checked. + * Asserts that a checkbox field in the current page is not checked. * * @param $id * Id of field to assert. @@ -3004,7 +3010,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a select option in the current page is not checked. + * Asserts that a select option in the current page is checked. * * @param $id * Id of select field to assert. @@ -3014,6 +3020,8 @@ class DrupalWebTestCase extends DrupalTestCase { * Message to display. * @return * TRUE on pass, FALSE on fail. + * + * @todo $id is unusable. Replace with $name. */ protected function assertOptionSelected($id, $option, $message = '') { $elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option)); @@ -3021,7 +3029,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a select option in the current page is not checked. + * Asserts that a select option in the current page is not checked. * * @param $id * Id of select field to assert. @@ -3038,7 +3046,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field exists with the given name or id. + * Asserts that a field exists with the given name or id. * * @param $field * Name or id of field to assert. @@ -3054,7 +3062,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that a field does not exist with the given name or id. + * Asserts that a field does not exist with the given name or id. * * @param $field * Name or id of field to assert. @@ -3070,7 +3078,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert that each HTML ID is used for just a single element. + * Asserts that each HTML ID is used for just a single element. * * @param $message * Message to display. @@ -3115,7 +3123,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert the page responds with the specified response code. + * Asserts the page responds with the specified response code. * * @param $code * Response code. For example 200 is a successful page request. For a list @@ -3132,7 +3140,7 @@ class DrupalWebTestCase extends DrupalTestCase { } /** - * Assert the page did not return the specified response code. + * Asserts the page did not return the specified response code. * * @param $code * Response code. For example 200 is a successful page request. For a list diff --git a/modules/simpletest/files/css_test_files/css_input_without_import.css b/modules/simpletest/files/css_test_files/css_input_without_import.css index a143846fb0f9b52b93407555afbeca90e7933296..5d455d1c61eb28af6e3b9597273b42ae18297f4f 100644 --- a/modules/simpletest/files/css_test_files/css_input_without_import.css +++ b/modules/simpletest/files/css_test_files/css_input_without_import.css @@ -1,4 +1,4 @@ -/* $Id: css_input_without_import.css,v 1.2 2009/11/10 17:27:53 webchick Exp $ */ +/* $Id: css_input_without_import.css,v 1.3 2010/10/08 15:36:12 dries Exp $ */ /** * @file Basic css that does not use import @@ -19,10 +19,9 @@ body { } /** - * CSS spec says that all whitespace is valid whitespace, so this selector should be just as - * good as the one above. + * CSS spec says that all whitespace is valid whitespace, so this selector + * should be just as good as the one above. */ - .this .is .a @@ -31,6 +30,39 @@ font: 1em/100% Verdana, sans-serif; color: #494949; } +some :pseudo .thing { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10'); + -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')"; +} + +::-moz-selection { + background: #000; + color:#fff; + +} +::selection { + background: #000; + color: #fff; +} + +@media print { + * { + background: #000 !important; + color: #fff !important; + } + @page { + margin: 0.5cm; + } +} + +@media screen and (max-device-width: 480px) { + background: #000; + color: #fff; +} + textarea, select { font: 1em/160% Verdana, sans-serif; color: #494949; diff --git a/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css b/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css index 6a90a8f76fd6bfa1493855c1610073fd092c5481..c19251143514c7b2a1096f14a5bc786e763c2a39 100644 --- a/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css +++ b/modules/simpletest/files/css_test_files/css_input_without_import.css.optimized.css @@ -6,4 +6,4 @@ body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this .is .a -.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} +.test{font:1em/100% Verdana,sans-serif;color:#494949;}some :pseudo .thing{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#000000,direction='180',strength='10');-ms-filter:"progid:DXImageTransform.Microsoft.Shadow(color=#000000,direction='180',strength='10')";}::-moz-selection{background:#000;color:#fff;}::selection{background:#000;color:#fff;}@media print{*{background:#000 !important;color:#fff !important;}@page{margin:0.5cm;}}@media screen and (max-device-width:480px){background:#000;color:#fff;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} diff --git a/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css b/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css index 3ddf8fae627de8f980ab1ec95d40f9bba4ebc57f..3366edd40797873fafd29c08db8a733894e2a824 100644 --- a/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css +++ b/modules/simpletest/files/css_test_files/css_input_without_import.css.unoptimized.css @@ -1,4 +1,4 @@ -/* $Id: css_input_without_import.css.unoptimized.css,v 1.2 2009/11/10 17:27:53 webchick Exp $ */ +/* $Id: css_input_without_import.css.unoptimized.css,v 1.3 2010/10/08 15:36:12 dries Exp $ */ /** * @file Basic css that does not use import @@ -19,10 +19,9 @@ body { } /** - * CSS spec says that all whitespace is valid whitespace, so this selector should be just as - * good as the one above. + * CSS spec says that all whitespace is valid whitespace, so this selector + * should be just as good as the one above. */ - .this .is .a @@ -31,6 +30,39 @@ font: 1em/100% Verdana, sans-serif; color: #494949; } +some :pseudo .thing { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10'); + -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')"; +} + +::-moz-selection { + background: #000; + color:#fff; + +} +::selection { + background: #000; + color: #fff; +} + +@media print { + * { + background: #000 !important; + color: #fff !important; + } + @page { + margin: 0.5cm; + } +} + +@media screen and (max-device-width: 480px) { + background: #000; + color: #fff; +} + textarea, select { font: 1em/160% Verdana, sans-serif; color: #494949; diff --git a/modules/simpletest/simpletest.css b/modules/simpletest/simpletest.css index 90b54ccf3e48fef4bdd4162089a9fb0d00c8b9eb..588fff6c6f4711580be661692874c521af790b25 100644 --- a/modules/simpletest/simpletest.css +++ b/modules/simpletest/simpletest.css @@ -1,28 +1,41 @@ -/* $Id: simpletest.css,v 1.8 2010/03/06 06:39:01 dries Exp $ */ - -/* Addon for the simpletest module */ -#simpletest { -} +/* $Id: simpletest.css,v 1.10 2010/10/09 18:09:48 webchick Exp $ */ /* Test Table */ #simpletest-form-table th.select-all { - width: 25px; + width: 1em; } - th.simpletest_test { - width: 160px; + width: 16em; } -table#simpletest-form-table tr td { +.simpletest-image { + display: inline-block; + cursor: pointer; + width: 1em; +} +.simpletest-group-label label { + display: inline; + font-weight: bold; +} +.simpletest-test-label label { + margin-left: 1em; /* LTR */ +} +.simpletest-test-description .description { + margin: 0; +} +#simpletest-form-table tr td { background-color: white; color: #494949; } - -table#simpletest-form-table tr.simpletest-group td { +#simpletest-form-table tr.simpletest-group td { background-color: #EDF5FA; color: #494949; } +table#simpletest-form-table tr.simpletest-group label { + display: inline; +} + div.message > div.item-list { font-weight: normal; } @@ -30,46 +43,33 @@ div.message > div.item-list { div.simpletest-pass { color: #33a333; } - .simpletest-fail { color: #981010; } tr.simpletest-pass.odd { - background: #b6ffb6; + background-color: #b6ffb6; } - tr.simpletest-pass.even { - background: #9bff9b; + background-color: #9bff9b; } - tr.simpletest-fail.odd { - background: #ffc9c9; + background-color: #ffc9c9; } - tr.simpletest-fail.even { - background: #ffacac; + background-color: #ffacac; } - tr.simpletest-exception.odd { - background: #f4ea71; + background-color: #f4ea71; } - tr.simpletest-exception.even { - background: #f5e742; + background-color: #f5e742; } - tr.simpletest-debug.odd { - background: #eeeeee; + background-color: #eee; } - tr.simpletest-debug.even { - background: #ffffff; -} - -div.simpletest-image { - display: inline; - cursor: pointer; + background-color: #fff; } a.simpletest-collapse { @@ -77,9 +77,7 @@ a.simpletest-collapse { width: 0; top: -99em; position: absolute; - } - a.simpletest-collapse:focus, a.simpletest-collapse:hover { font-size: 80%; diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 80b7e1f4d55de3482f8e7fc25af09d12b42b2b91..99010a3270b224af4cb087d106aec0c7401326d2 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -1,4 +1,4 @@ -; $Id: simpletest.info,v 1.24 2010/10/05 20:04:19 webchick Exp $ +; $Id: simpletest.info,v 1.26 2010/10/22 16:36:14 webchick Exp $ name = Testing description = Provides a framework for unit and functional testing. package = Core @@ -19,6 +19,7 @@ files[] = tests/bootstrap.test files[] = tests/cache.test files[] = tests/common.test files[] = tests/database_test.test +files[] = tests/entity_crud_hook_test.test files[] = tests/entity_query.test files[] = tests/error.test files[] = tests/file.test @@ -40,13 +41,14 @@ files[] = tests/update.test files[] = tests/xmlrpc.test files[] = tests/upgrade/upgrade.test files[] = tests/upgrade/upgrade.comment.test +files[] = tests/upgrade/upgrade.filter.test files[] = tests/upgrade/upgrade.node.test files[] = tests/upgrade/upgrade.taxonomy.test files[] = tests/upgrade/upgrade.upload.test files[] = tests/upgrade/upgrade.locale.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/simpletest.install b/modules/simpletest/simpletest.install index 135de030a55916e2679cc15f77d4b7397b3b0fb0..ef813caa665a34d0bf646cdd515a2f590c2ae4a5 100644 --- a/modules/simpletest/simpletest.install +++ b/modules/simpletest/simpletest.install @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.install,v 1.37 2010/09/01 20:08:17 dries Exp $ +// $Id: simpletest.install,v 1.39 2010/10/15 23:40:21 webchick Exp $ /** * @file @@ -29,6 +29,8 @@ function simpletest_uninstall() { } /** + * Implements hook_requirements(). + * * Check that the cURL extension exists for PHP. */ function simpletest_requirements($phase) { @@ -37,7 +39,7 @@ function simpletest_requirements($phase) { $has_curl = function_exists('curl_init'); $has_hash = function_exists('hash_hmac'); - $has_domdocument = class_exists('DOMDocument'); + $has_domdocument = method_exists('DOMDocument', 'loadHTML'); $open_basedir = ini_get('open_basedir'); $requirements['curl'] = array( @@ -81,6 +83,9 @@ function simpletest_requirements($phase) { return $requirements; } +/** + * Implements hook_schema(). + */ function simpletest_schema() { $schema['simpletest'] = array( 'description' => 'Stores simpletest messages', diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index e06a4c5503eb9af55edba8e5b989e655013111f1..60f79fd3069e252ff0eef4899b2f65cd785ea67b 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.module,v 1.94 2010/09/24 00:37:44 dries Exp $ +// $Id: simpletest.module,v 1.95 2010/10/10 11:34:22 dries Exp $ /** * @file @@ -124,7 +124,6 @@ function _simpletest_format_summary_line($summary) { * drupal being the default. */ function simpletest_run_tests($test_list, $reporter = 'drupal') { - cache_clear_all(); $test_id = db_insert('simpletest_test_id') ->useDefaults(array('test_id')) ->execute(); diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc index 6030f2c47a76e064d61838403c4332eb9998e001..4f533511bbfa5defeae3d95b52474412dfd1d7e0 100644 --- a/modules/simpletest/simpletest.pages.inc +++ b/modules/simpletest/simpletest.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: simpletest.pages.inc,v 1.31 2010/07/24 17:28:26 dries Exp $ +// $Id: simpletest.pages.inc,v 1.33 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -108,12 +108,15 @@ function theme_simpletest_test_table($variables) { // Expand/collapse image and group title. $row[] = array( - 'data' => '<div class="simpletest-image" id="simpletest-test-group-' . $test_class . '"></div> ' . - '<label for="' . $test_class . '-select-all" class="simpletest-group-label">' . $key . '</label>', - 'style' => 'font-weight: bold;' + 'data' => '<div class="simpletest-image" id="simpletest-test-group-' . $test_class . '"></div>' . + '<label for="' . $test_class . '-select-all" class="simpletest-group-label">' . $key . '</label>', + 'class' => array('simpletest-group-label'), ); - $row[] = ' '; + $row[] = array( + 'data' => ' ', + 'class' => array('simpletest-group-description'), + ); $rows[] = array('data' => $row, 'class' => array('simpletest-group')); @@ -139,15 +142,24 @@ function theme_simpletest_test_table($variables) { $title = $test['#title']; $description = $test['#description']; - unset($test['#title']); + $test['#title_display'] = 'invisible'; unset($test['#description']); // Test name is used to determine what tests to run. $test['#name'] = $test_name; - $row[] = drupal_render($test); - $row[] = theme('indentation', array('size' => 1)) . '<label for="' . $test['#id'] . '">' . $title . '</label>'; - $row[] = '<div class="description">' . $description . '</div>'; + $row[] = array( + 'data' => drupal_render($test), + 'class' => array('simpletest-test-select'), + ); + $row[] = array( + 'data' => '<label for="' . $test['#id'] . '">' . $title . '</label>', + 'class' => array('simpletest-test-label'), + ); + $row[] = array( + 'data' => '<div class="description">' . $description . '</div>', + 'class' => array('simpletest-test-description'), + ); $rows[] = array('data' => $row, 'class' => array($test_class . '-test', ($collapsed ? 'js-hide' : ''))); } diff --git a/modules/simpletest/tests/actions_loop_test.info b/modules/simpletest/tests/actions_loop_test.info index 342f7bea6d77f5051aebf2af805e161e7a163417..2a72aafe31f38b3ee5bb50633db65bf2654cc1ec 100644 --- a/modules/simpletest/tests/actions_loop_test.info +++ b/modules/simpletest/tests/actions_loop_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = actions_loop_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test index 836daf36bb2723c8816b8ac8d5b9965a5b965e2a..028975e016d0849aa2af8c4a35e293a43b337510 100644 --- a/modules/simpletest/tests/ajax.test +++ b/modules/simpletest/tests/ajax.test @@ -1,5 +1,5 @@ <?php -// $Id: ajax.test,v 1.19 2010/10/04 17:46:01 dries Exp $ +// $Id: ajax.test,v 1.20 2010/10/21 19:31:39 dries Exp $ class AJAXTestCase extends DrupalWebTestCase { function setUp() { @@ -59,6 +59,8 @@ class AJAXTestCase extends DrupalWebTestCase { * Tests primary AJAX framework functions. */ class AJAXFrameworkTestCase extends AJAXTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'AJAX framework', @@ -84,6 +86,12 @@ class AJAXFrameworkTestCase extends AJAXTestCase { 'settings' => array('basePath' => base_path(), 'ajax' => 'test'), ); $this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().')); + + // Verify that AJAX settings are loaded for #type 'link'. + $this->drupalGet('ajax-test/link'); + $settings = $this->drupalGetSettings(); + $this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips')); + $this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main'); } /** diff --git a/modules/simpletest/tests/ajax_forms_test.info b/modules/simpletest/tests/ajax_forms_test.info index 7317778e9b8f74c2be02c87674dd6bed42d16799..9dff275cfc0171602fd80f936301e8fd0dfe174c 100644 --- a/modules/simpletest/tests/ajax_forms_test.info +++ b/modules/simpletest/tests/ajax_forms_test.info @@ -7,8 +7,8 @@ files[] = ajax_forms_test.module version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/ajax_test.info b/modules/simpletest/tests/ajax_test.info index 2bff63efe4f7319650a0d7e60608d4b3bde328f0..83d1d501cd7219e56b4d5458b858d424a36e3f8d 100644 --- a/modules/simpletest/tests/ajax_test.info +++ b/modules/simpletest/tests/ajax_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = ajax_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/ajax_test.module b/modules/simpletest/tests/ajax_test.module index 21762bdac558bc3f15cc247089de3eb9129c6bbe..4b0e19e05e39673dc6c7f0860ef46d9a1c17bff1 100644 --- a/modules/simpletest/tests/ajax_test.module +++ b/modules/simpletest/tests/ajax_test.module @@ -1,5 +1,5 @@ <?php -// $Id: ajax_test.module,v 1.4 2010/10/04 17:46:01 dries Exp $ +// $Id: ajax_test.module,v 1.5 2010/10/21 19:31:39 dries Exp $ /** * @file @@ -24,6 +24,11 @@ function ajax_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['ajax-test/link'] = array( + 'title' => 'AJAX Link', + 'page callback' => 'ajax_test_link', + 'access callback' => TRUE, + ); return $items; } @@ -49,3 +54,19 @@ function ajax_test_error() { } return array('#type' => 'ajax', '#error' => $message); } + +/** + * Menu callback; Renders a #type link with #ajax. + */ +function ajax_test_link() { + $build['link'] = array( + '#type' => 'link', + '#title' => 'Show help', + '#href' => 'filter/tips', + '#ajax' => array( + 'wrapper' => 'block-system-main', + ), + ); + return $build; +} + diff --git a/modules/simpletest/tests/batch_test.info b/modules/simpletest/tests/batch_test.info index 4ed7d68cffdd1e51d930cf84af45a0ce8f7140fa..659aeba64547ea09fa0220c7168498943ed7f81d 100644 --- a/modules/simpletest/tests/batch_test.info +++ b/modules/simpletest/tests/batch_test.info @@ -8,8 +8,8 @@ files[] = batch_test.module files[] = batch_test.callbacks.inc hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test index 61e8e05c3fb252a114de8336bbb38dd0b331810c..2c9bca895444ed1e196c463bfdc032cb9359425e 100644 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -1,5 +1,5 @@ <?php -// $Id: common.test,v 1.129 2010/10/05 19:59:10 dries Exp $ +// $Id: common.test,v 1.130 2010/10/08 15:36:12 dries Exp $ /** * @file @@ -862,11 +862,10 @@ class CascadingStylesheetsUnitTest extends DrupalUnitTestCase { /** * Tests basic CSS loading with and without optimization via drupal_load_stylesheet(). * - * This can be enhanced by adding additional CSS files with variant test cases. - * Currently, this is specifically testing to make sure that whitespace - * is treated with adequate respect (see http://drupal.org/node/472820) and - * that image paths in imported files are preserved (see - * http://drupal.org/node/265719). + * Known tests: + * - Retain white-space in selectors. (http://drupal.org/node/472820) + * - Proper URLs in imported files. (http://drupal.org/node/265719) + * - Retain pseudo-selectors. (http://drupal.org/node/460448) */ function testLoadCssBasic() { // Array of files to test living in 'simpletest/files/css_test_files/'. @@ -882,10 +881,16 @@ class CascadingStylesheetsUnitTest extends DrupalUnitTestCase { foreach ($testfiles as $file) { $expected = file_get_contents("$path/$file.unoptimized.css"); $unoptimized_output = drupal_load_stylesheet("$path/$file.unoptimized.css", FALSE); + $this->verbose('Expected:<pre>' . $expected . '</pre>' + . 'Actual:<pre>' . $unoptimized_output . '</pre>' + ); $this->assertEqual($unoptimized_output, $expected, t('Unoptimized CSS file has expected contents (@file)', array('@file' => $file))); $expected = file_get_contents("$path/$file.optimized.css"); $optimized_output = drupal_load_stylesheet("$path/$file", TRUE); + $this->verbose('Expected:<pre>' . $expected . '</pre>' + . 'Actual:<pre>' . $optimized_output . '</pre>' + ); $this->assertEqual($optimized_output, $expected, t('Optimized CSS file has expected contents (@file)', array('@file' => $file))); } } diff --git a/modules/simpletest/tests/common_test.info b/modules/simpletest/tests/common_test.info index 25091744c9633664395208aef272906ead3d3cc4..8bbbb0cabbf096fc8497f79822a65b7cbfa7fdf3 100644 --- a/modules/simpletest/tests/common_test.info +++ b/modules/simpletest/tests/common_test.info @@ -9,8 +9,8 @@ stylesheets[all][] = common_test.css stylesheets[print][] = common_test.print.css hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/database_test.info b/modules/simpletest/tests/database_test.info index b3426f746daadd3f737f2dc20b68de221718b778..45a400334925476400e79365c3767782bf5783a0 100644 --- a/modules/simpletest/tests/database_test.info +++ b/modules/simpletest/tests/database_test.info @@ -8,8 +8,8 @@ files[] = database_test.install version = VERSION hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/database_test.module b/modules/simpletest/tests/database_test.module index 06748304640041d7f6058ea159b4a1c23faffd43..0376930308788ce350a790b6b1a861327af75e94 100644 --- a/modules/simpletest/tests/database_test.module +++ b/modules/simpletest/tests/database_test.module @@ -1,5 +1,5 @@ <?php -// $Id: database_test.module,v 1.14 2010/06/25 19:33:47 dries Exp $ +// $Id: database_test.module,v 1.15 2010/10/15 04:34:15 webchick Exp $ /** * Implements hook_query_alter(). @@ -66,7 +66,11 @@ function database_test_menu() { 'access callback' => TRUE, 'page callback' => 'database_test_tablesort_first', ); - + $items['database_test/tablesort_default_sort'] = array( + 'access callback' => TRUE, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('database_test_theme_tablesort'), + ); return $items; } @@ -191,3 +195,48 @@ function database_test_tablesort_first() { )); exit; } + +/** + * Output a form without setting a header sort. + */ +function database_test_theme_tablesort($form, &$form_state) { + $header = array( + 'username' => array('data' => t('Username'), 'field' => 'u.name'), + 'status' => array('data' => t('Status'), 'field' => 'u.status'), + ); + + $query = db_select('users', 'u'); + $query->condition('u.uid', 0, '<>'); + user_build_filter_query($query); + + $count_query = clone $query; + $count_query->addExpression('COUNT(u.uid)'); + + $query = $query->extend('PagerDefault')->extend('TableSort'); + $query + ->fields('u', array('uid', 'name', 'status', 'created', 'access')) + ->limit(50) + ->orderByHeader($header) + ->setCountQuery($count_query); + $result = $query->execute(); + + $options = array(); + + $status = array(t('blocked'), t('active')); + $accounts = array(); + foreach ($result as $account) { + $options[$account->uid] = array( + 'username' => check_plain($account->name), + 'status' => $status[$account->status], + ); + } + + $form['accounts'] = array( + '#type' => 'tableselect', + '#header' => $header, + '#options' => $options, + '#empty' => t('No people available.'), + ); + + return $form; +} diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test index 5418d6ab8c238176e3f743c10f65345ac843a27d..af96b3263db2d5756afc753e3e3ae98b9795db36 100644 --- a/modules/simpletest/tests/database_test.test +++ b/modules/simpletest/tests/database_test.test @@ -1,5 +1,5 @@ <?php -// $Id: database_test.test,v 1.103 2010/09/24 02:05:55 dries Exp $ +// $Id: database_test.test,v 1.104 2010/10/15 04:34:15 webchick Exp $ /** * Dummy class for fetching into a class. @@ -2314,6 +2314,15 @@ class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase { $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort']))); } } + + /** + * Confirm that if a sort is not set in a tableselect form there is no error thrown when using the default. + */ + function testTableSortDefaultSort() { + $this->drupalGet('database_test/tablesort_default_sort'); + // Any PHP errors or notices thrown would trigger a simpletest exception, so + // no additional assertions are needed. + } } /** diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/simpletest/tests/entity_cache_test.info index b34ccedc81cf686962e23662df590bef7f87f899..e1ff7e3ca602408d51330502b6b38e354dc97f62 100644 --- a/modules/simpletest/tests/entity_cache_test.info +++ b/modules/simpletest/tests/entity_cache_test.info @@ -8,8 +8,8 @@ files[] = entity_cache_test.module dependencies[] = entity_cache_test_dependency hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/entity_cache_test.module b/modules/simpletest/tests/entity_cache_test.module index acafca109d0acc1fe41291ae108f722df0d6fa48..244e2f8b74685ccae54b5d47678b39dc425d043d 100644 --- a/modules/simpletest/tests/entity_cache_test.module +++ b/modules/simpletest/tests/entity_cache_test.module @@ -1,5 +1,5 @@ <?php -// $Id: entity_cache_test.module,v 1.1 2010/04/18 15:01:56 webchick Exp $ +// $Id: entity_cache_test.module,v 1.2 2010/10/15 04:44:08 webchick Exp $ /** * @file @@ -9,14 +9,20 @@ /** * Implements hook_watchdog(). * - * This function is called during module_enable() and tries to access entity - * information provided by the module this one depends on. The information is - * stored in a temporary system variable and is later analyzed in the test - * case. + * This hook is called during module_enable() and since this hook + * implementation is invoked, we have to expect that this module and dependent + * modules have been properly installed already. So we expect to be able to + * retrieve the entity information that has been registered by the required + * dependency module. * * @see EnableDisableTestCase::testEntityCache() + * @see entity_cache_test_dependency_entity_info() */ -function entity_cache_test_watchdog() { - $info = entity_get_info('entity_cache_test'); - variable_set('entity_cache_test', $info); +function entity_cache_test_watchdog($log_entry) { + if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') { + $info = entity_get_info('entity_cache_test'); + // Store the information in a system variable to analyze it later in the + // test case. + variable_set('entity_cache_test', $info); + } } diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/simpletest/tests/entity_cache_test_dependency.info index d20d42503d15c4986cfca3f0ff9a85b72165936e..08b039a7f6e0e2e50f6583147a4b2f65c1cbe4b3 100644 --- a/modules/simpletest/tests/entity_cache_test_dependency.info +++ b/modules/simpletest/tests/entity_cache_test_dependency.info @@ -7,8 +7,8 @@ core = 7.x files[] = entity_cache_test_dependency.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/entity_crud_hook_test.info b/modules/simpletest/tests/entity_crud_hook_test.info new file mode 100644 index 0000000000000000000000000000000000000000..7d6d2b7bce507e34e5cbb4ba9fba4d5ebf05d1fb --- /dev/null +++ b/modules/simpletest/tests/entity_crud_hook_test.info @@ -0,0 +1,15 @@ +; $Id: entity_crud_hook_test.info,v 1.1 2010/10/15 03:36:21 webchick Exp $ + +name = "Entity CRUD Hooks Test" +description = "Support module for CRUD hook tests." +core = 7.x +package = Testing +files[] = entity_crud_hook_test.module +version = VERSION +hidden = TRUE + +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" +project = "drupal" +datestamp = "1287812130" + diff --git a/modules/simpletest/tests/entity_crud_hook_test.module b/modules/simpletest/tests/entity_crud_hook_test.module new file mode 100644 index 0000000000000000000000000000000000000000..9b7f9d90645ba10e0f9f9d635b629169cf75f583 --- /dev/null +++ b/modules/simpletest/tests/entity_crud_hook_test.module @@ -0,0 +1,214 @@ +<?php +// $Id: entity_crud_hook_test.module,v 1.1 2010/10/15 03:36:21 webchick Exp $ + +// +// Insert hooks +// + +/** + * Implements hook_entity_insert(). + */ +function entity_crud_hook_test_entity_insert($entity, $type) { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type); +} + +/** + * Implements hook_comment_insert(). + */ +function entity_crud_hook_test_comment_insert() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_file_insert(). + */ +function entity_crud_hook_test_file_insert() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_node_insert(). + */ +function entity_crud_hook_test_node_insert() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_term_insert(). + */ +function entity_crud_hook_test_taxonomy_term_insert() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_vocabulary_insert(). + */ +function entity_crud_hook_test_taxonomy_vocabulary_insert() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_user_insert(). + */ +function entity_crud_hook_test_user_insert() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +// +// Load hooks +// + +/** + * Implements hook_entity_load(). + */ +function entity_crud_hook_test_entity_load(array $entities, $type) { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type); +} + +/** + * Implements hook_comment_load(). + */ +function entity_crud_hook_test_comment_load() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_file_load(). + */ +function entity_crud_hook_test_file_load() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_node_load(). + */ +function entity_crud_hook_test_node_load() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_term_load(). + */ +function entity_crud_hook_test_taxonomy_term_load() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_vocabulary_load(). + */ +function entity_crud_hook_test_taxonomy_vocabulary_load() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_user_load(). + */ +function entity_crud_hook_test_user_load() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +// +// Update hooks +// + +/** + * Implements hook_entity_update(). + */ +function entity_crud_hook_test_entity_update($entity, $type) { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type); +} + +/** + * Implements hook_comment_update(). + */ +function entity_crud_hook_test_comment_update() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_file_update(). + */ +function entity_crud_hook_test_file_update() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_node_update(). + */ +function entity_crud_hook_test_node_update() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_term_update(). + */ +function entity_crud_hook_test_taxonomy_term_update() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_vocabulary_update(). + */ +function entity_crud_hook_test_taxonomy_vocabulary_update() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_user_update(). + */ +function entity_crud_hook_test_user_update() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +// +// Delete hooks +// + +/** + * Implements hook_entity_delete(). + */ +function entity_crud_hook_test_entity_delete($entity, $type) { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type); +} + +/** + * Implements hook_comment_delete(). + */ +function entity_crud_hook_test_comment_delete() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_file_delete(). + */ +function entity_crud_hook_test_file_delete() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_node_delete(). + */ +function entity_crud_hook_test_node_delete() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_term_delete(). + */ +function entity_crud_hook_test_taxonomy_term_delete() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_taxonomy_vocabulary_delete(). + */ +function entity_crud_hook_test_taxonomy_vocabulary_delete() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} + +/** + * Implements hook_user_delete(). + */ +function entity_crud_hook_test_user_delete() { + $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called'); +} diff --git a/modules/simpletest/tests/entity_crud_hook_test.test b/modules/simpletest/tests/entity_crud_hook_test.test new file mode 100644 index 0000000000000000000000000000000000000000..21b10a43b7c82272a02f03a433365c160d5d6ec9 --- /dev/null +++ b/modules/simpletest/tests/entity_crud_hook_test.test @@ -0,0 +1,309 @@ +<?php +// $Id: entity_crud_hook_test.test,v 1.1 2010/10/15 03:36:21 webchick Exp $ + +/** + * Test invocation of hooks when inserting, loading, updating or deleting an + * entity. Tested hooks are: + * - hook_entity_insert() + * - hook_entity_load() + * - hook_entity_update() + * - hook_entity_delete() + * As well as all type-specific hooks, like hook_node_insert(), + * hook_comment_update(), etc. + */ +class EntityCrudHookTestCase extends DrupalWebTestCase { + + protected $ids = array(); + + public static function getInfo() { + return array( + 'name' => 'Entity CRUD hooks', + 'description' => 'Tests the invocation of hooks when inserting, loading, updating or deleting an entity.', + 'group' => 'Entity API', + ); + } + + public function setUp() { + parent::setUp('entity_crud_hook_test', 'taxonomy', 'comment'); + } + + /** + * Pass if the message $text was set by one of the CRUD hooks in + * entity_crud_hook_test.module, i.e., if the $text is an element of + * $_SESSION['entity_crud_hook_test']. + * + * @param $text + * Plain text to look for. + * @param $message + * Message to display. + * @param $group + * The group this message belongs to, defaults to 'Other'. + * @return + * TRUE on pass, FALSE on fail. + */ + protected function assertHookMessage($text, $message = NULL, $group = 'Other') { + if (!isset($message)) { + $message = $text; + } + return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group); + } + + /** + * Test hook invocations for CRUD operations on comments. + */ + public function testCommentHooks() { + $node = (object) array( + 'uid' => 1, + 'type' => 'article', + 'title' => 'Test node', + 'status' => 1, + 'comment' => 2, + 'promote' => 0, + 'sticky' => 0, + 'language' => LANGUAGE_NONE, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ); + node_save($node); + $nid = $node->nid; + + $comment = (object) array( + 'cid' => NULL, + 'pid' => 0, + 'nid' => $nid, + 'uid' => 1, + 'subject' => 'Test comment', + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + 'status' => 1, + 'language' => LANGUAGE_NONE, + ); + $_SESSION['entity_crud_hook_test'] = array(); + comment_save($comment); + + $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment'); + $this->assertHookMessage('entity_crud_hook_test_comment_insert called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $comment = comment_load($comment->cid); + + $this->assertHookMessage('entity_crud_hook_test_entity_load called for type comment'); + $this->assertHookMessage('entity_crud_hook_test_comment_load called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $comment->subject = 'New subject'; + comment_save($comment); + + $this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment'); + $this->assertHookMessage('entity_crud_hook_test_comment_update called'); + + $_SESSION['entity_crud_hook_test'] = array(); + comment_delete($comment->cid); + + $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment'); + $this->assertHookMessage('entity_crud_hook_test_comment_delete called'); + } + + /** + * Test hook invocations for CRUD operations on files. + */ + public function testFileHooks() { + $url = 'public://entity_crud_hook_test.file'; + file_put_contents($url, 'Test test test'); + $file = (object) array( + 'fid' => NULL, + 'uid' => 1, + 'filename' => 'entity_crud_hook_test.file', + 'uri' => $url, + 'filemime' => 'text/plain', + 'filesize' => filesize($url), + 'status' => 1, + 'timestamp' => REQUEST_TIME, + ); + $_SESSION['entity_crud_hook_test'] = array(); + file_save($file); + + $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file'); + $this->assertHookMessage('entity_crud_hook_test_file_insert called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $file = file_load($file->fid); + + $this->assertHookMessage('entity_crud_hook_test_entity_load called for type file'); + $this->assertHookMessage('entity_crud_hook_test_file_load called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $file->filename = 'new.entity_crud_hook_test.file'; + file_save($file); + + $this->assertHookMessage('entity_crud_hook_test_entity_update called for type file'); + $this->assertHookMessage('entity_crud_hook_test_file_update called'); + + $_SESSION['entity_crud_hook_test'] = array(); + file_delete($file); + + $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file'); + $this->assertHookMessage('entity_crud_hook_test_file_delete called'); + } + + /** + * Test hook invocations for CRUD operations on nodes. + */ + public function testNodeHooks() { + $node = (object) array( + 'uid' => 1, + 'type' => 'article', + 'title' => 'Test node', + 'status' => 1, + 'comment' => 2, + 'promote' => 0, + 'sticky' => 0, + 'language' => LANGUAGE_NONE, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ); + $_SESSION['entity_crud_hook_test'] = array(); + node_save($node); + + $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node'); + $this->assertHookMessage('entity_crud_hook_test_node_insert called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $node = node_load($node->nid); + + $this->assertHookMessage('entity_crud_hook_test_entity_load called for type node'); + $this->assertHookMessage('entity_crud_hook_test_node_load called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $node->title = 'New title'; + node_save($node); + + $this->assertHookMessage('entity_crud_hook_test_entity_update called for type node'); + $this->assertHookMessage('entity_crud_hook_test_node_update called'); + + $_SESSION['entity_crud_hook_test'] = array(); + node_delete($node->nid); + + $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node'); + $this->assertHookMessage('entity_crud_hook_test_node_delete called'); + } + + /** + * Test hook invocations for CRUD operations on taxonomy terms. + */ + public function testTaxonomyTermHooks() { + $vocabulary = (object) array( + 'name' => 'Test vocabulary', + 'machine_name' => 'test', + 'description' => NULL, + 'module' => 'entity_crud_hook_test', + ); + taxonomy_vocabulary_save($vocabulary); + + $term = (object) array( + 'vid' => $vocabulary->vid, + 'name' => 'Test term', + 'description' => NULL, + 'format' => 1, + ); + $_SESSION['entity_crud_hook_test'] = array(); + taxonomy_term_save($term); + + $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $term = taxonomy_term_load($term->tid); + + $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_term'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_load called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $term->name = 'New name'; + taxonomy_term_save($term); + + $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called'); + + $_SESSION['entity_crud_hook_test'] = array(); + taxonomy_term_delete($term->tid); + + $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called'); + } + + /** + * Test hook invocations for CRUD operations on taxonomy vocabularies. + */ + public function testTaxonomyVocabularyHooks() { + $vocabulary = (object) array( + 'name' => 'Test vocabulary', + 'machine_name' => 'test', + 'description' => NULL, + 'module' => 'entity_crud_hook_test', + ); + $_SESSION['entity_crud_hook_test'] = array(); + taxonomy_vocabulary_save($vocabulary); + + $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $vocabulary = taxonomy_vocabulary_load($vocabulary->vid); + + $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_vocabulary'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_load called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $vocabulary->name = 'New name'; + taxonomy_vocabulary_save($vocabulary); + + $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called'); + + $_SESSION['entity_crud_hook_test'] = array(); + taxonomy_vocabulary_delete($vocabulary->vid); + + $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary'); + $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called'); + } + + /** + * Test hook invocations for CRUD operations on users. + */ + public function testUserHooks() { + $edit = array( + 'name' => 'Test user', + 'mail' => 'test@example.com', + 'created' => REQUEST_TIME, + 'status' => 1, + 'language' => 'en', + ); + $account = (object) $edit; + $_SESSION['entity_crud_hook_test'] = array(); + $account = user_save($account, $edit); + + $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user'); + $this->assertHookMessage('entity_crud_hook_test_user_insert called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $account = user_load($account->uid); + + $this->assertHookMessage('entity_crud_hook_test_entity_load called for type user'); + $this->assertHookMessage('entity_crud_hook_test_user_load called'); + + $_SESSION['entity_crud_hook_test'] = array(); + $edit['name'] = 'New name'; + $account = user_save($account, $edit); + + $this->assertHookMessage('entity_crud_hook_test_entity_update called for type user'); + $this->assertHookMessage('entity_crud_hook_test_user_update called'); + + $_SESSION['entity_crud_hook_test'] = array(); + user_delete($account->uid); + + $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user'); + $this->assertHookMessage('entity_crud_hook_test_user_delete called'); + } + +} diff --git a/modules/simpletest/tests/error.test b/modules/simpletest/tests/error.test index 2ebecb28d0c9bba2428af52052f16b39915e1fcc..d6a2fb7f911041dc44e727cda4df61009d3dd260 100644 --- a/modules/simpletest/tests/error.test +++ b/modules/simpletest/tests/error.test @@ -1,5 +1,5 @@ <?php -// $Id: error.test,v 1.8 2010/08/05 23:53:38 webchick Exp $ +// $Id: error.test,v 1.9 2010/10/16 00:00:16 webchick Exp $ /** * Tests Drupal error and exception handlers. @@ -23,21 +23,21 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { function testErrorHandler() { $error_notice = array( '%type' => 'Notice', - '%message' => 'Undefined variable: bananas', + '!message' => 'Undefined variable: bananas', '%function' => 'error_test_generate_warnings()', '%line' => 44, '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $error_warning = array( '%type' => 'Warning', - '%message' => 'Division by zero', + '!message' => 'Division by zero', '%function' => 'error_test_generate_warnings()', '%line' => 46, '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $error_user_notice = array( '%type' => 'User warning', - '%message' => 'Drupal is awesome', + '!message' => 'Drupal is awesome', '%function' => 'error_test_generate_warnings()', '%line' => 48, '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), @@ -74,14 +74,14 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { function testExceptionHandler() { $error_exception = array( '%type' => 'Exception', - '%message' => 'Drupal is awesome', + '!message' => 'Drupal is awesome', '%function' => 'error_test_trigger_exception()', '%line' => 57, '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), ); $error_pdo_exception = array( '%type' => 'PDOException', - '%message' => 'SELECT * FROM bananas_are_awesome', + '!message' => 'SELECT * FROM bananas_are_awesome', '%function' => 'error_test_trigger_pdo_exception()', '%line' => 65, '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'), @@ -96,7 +96,7 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { // We cannot use assertErrorMessage() since the extact error reported // varies from database to database. Check that the SQL string is displayed. $this->assertText($error_pdo_exception['%type'], t('Found %type in error page.', $error_pdo_exception)); - $this->assertText($error_pdo_exception['%message'], t('Found %message in error page.', $error_pdo_exception)); + $this->assertText($error_pdo_exception['!message'], t('Found !message in error page.', $error_pdo_exception)); $error_details = t('in %function (line %line of %file)', $error_pdo_exception); $this->assertRaw($error_details, t("Found '!message' in error page.", array('!message' => $error_details))); } @@ -105,7 +105,7 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { * Helper function: assert that the error message is found. */ function assertErrorMessage(array $error) { - $message = t('%type: %message in %function (line %line of %file).', $error); + $message = t('%type: !message in %function (line %line of %file).', $error); $this->assertRaw($message, t('Error !message found.', array('!message' => $message))); } @@ -113,7 +113,7 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase { * Helper function: assert that the error message is not found. */ function assertNoErrorMessage(array $error) { - $message = t('%type: %message in %function (line %line of %file).', $error); + $message = t('%type: !message in %function (line %line of %file).', $error); $this->assertNoRaw($message, t('Error !message not found.', array('!message' => $message))); } } diff --git a/modules/simpletest/tests/error_test.info b/modules/simpletest/tests/error_test.info index 5560212aae1b3aebcc779b32639332da561ce79e..219ffb89a1f83797afdc76653b6c716a95d14368 100644 --- a/modules/simpletest/tests/error_test.info +++ b/modules/simpletest/tests/error_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = error_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/file_test.info b/modules/simpletest/tests/file_test.info index fd0413fe18477b0c111b7852dc34f6257df10642..6bed847e30139c1cf29bbca388bba53f0a804978 100644 --- a/modules/simpletest/tests/file_test.info +++ b/modules/simpletest/tests/file_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = file_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/filter_test.info b/modules/simpletest/tests/filter_test.info index 5e0c784a8f055bfdfd0e1639f3bb47d5ccf63e93..d6cc777667c6bfd6afdf46ddcba8747c190e4380 100644 --- a/modules/simpletest/tests/filter_test.info +++ b/modules/simpletest/tests/filter_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = filter_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/form_test.info b/modules/simpletest/tests/form_test.info index 08738f2a491371c4dab06377f4fa0f517d71237d..053bbf12cc90bfdeaadf83dd47267faece01870a 100644 --- a/modules/simpletest/tests/form_test.info +++ b/modules/simpletest/tests/form_test.info @@ -8,8 +8,8 @@ files[] = form_test.module files[] = form_test.file.inc hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index 1788bc254cf5a67d8328226d417f7df985be3c7d..3ae8f65a6b7b68cee1a7c4d438de7ce9ca2b1443 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -1,5 +1,5 @@ <?php -// $Id: form_test.module,v 1.51 2010/10/04 18:00:46 dries Exp $ +// $Id: form_test.module,v 1.52 2010/10/20 01:15:58 dries Exp $ /** * @file @@ -999,14 +999,14 @@ function _form_test_disabled_elements($form, &$form_state) { '#title' => 'Text format', '#disabled' => TRUE, '#default_value' => 'Text value', - '#format' => 1, + '#format' => 'plain_text', '#expected_value' => array( 'value' => 'Text value', - 'format' => 1, + 'format' => 'plain_text', ), '#test_hijack_value' => array( 'value' => 'HIJACK', - 'format' => 2, + 'format' => 'filtered_html', ), ); diff --git a/modules/simpletest/tests/image_test.info b/modules/simpletest/tests/image_test.info index 0caf3c805569b149369db100241e42a7aa143afd..b90879ab1b82698e56a9fa333054cc4db1d866b1 100644 --- a/modules/simpletest/tests/image_test.info +++ b/modules/simpletest/tests/image_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = image_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test index 81643ded67fa8a3ce89256bcea331aa8bcaf539d..0c002e2e9c5f4c7d5a2e4b2351cceac031296d36 100644 --- a/modules/simpletest/tests/menu.test +++ b/modules/simpletest/tests/menu.test @@ -1,5 +1,5 @@ <?php -// $Id: menu.test,v 1.36 2010/10/01 15:24:18 webchick Exp $ +// $Id: menu.test,v 1.38 2010/10/15 04:46:58 webchick Exp $ /** * @file @@ -431,6 +431,39 @@ class MenuRouterTestCase extends DrupalWebTestCase { $this->assertRaw('title="Test title attribute"', t('Title attribute of a menu link renders.')); $this->assertRaw('testparam=testvalue', t('Query parameter added to menu link.')); } + + /** + * Tests the possible ways to set the title for menu items. + * Also tests that menu item titles work with string overrides. + */ + function testMenuItemTitlesCases() { + + // Build array with string overrides. + $test_data = array( + 1 => array('Example title - Case 1' => 'Alternative example title - Case 1'), + 2 => array('Example @sub1 - Case @op2' => 'Alternative example @sub1 - Case @op2'), + 3 => array('Example title' => 'Alternative example title'), + 4 => array('Example title' => 'Alternative example title'), + ); + + foreach ($test_data as $case_no => $override) { + $this->menuItemTitlesCasesHelper($case_no); + variable_set('locale_custom_strings_en', array('' => $override)); + $this->menuItemTitlesCasesHelper($case_no, TRUE); + variable_set('locale_custom_strings_en', array()); + } + } + + /** + * Get a url and assert the title given a case number. If override is true, + * the title is asserted to begin with "Alternative". + */ + private function menuItemTitlesCasesHelper($case_no, $override = FALSE) { + $this->drupalGet('menu-title-test/case' . $case_no); + $this->assertResponse(200); + $asserted_title = $override ? 'Alternative example title - Case ' . $case_no : 'Example title - Case ' . $case_no; + $this->assertTitle($asserted_title . ' | Drupal', t('Menu title is') . ': ' . $asserted_title, 'Menu'); + } } /** @@ -872,19 +905,50 @@ class MenuBreadcrumbTestCase extends DrupalWebTestCase { // Verify breadcrumb on front page. $this->assertBreadcrumb('<front>', array()); + // Verify breadcrumb on user pages (without menu link) for anonymous user. $trail = $home; $this->assertBreadcrumb('user', $trail, t('User account')); $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name); + // Verify breadcrumb on user pages (without menu link) for registered users. $this->drupalLogin($this->admin_user); + $trail = $home; + $this->assertBreadcrumb('user', $trail, $this->admin_user->name); + $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name); + $trail += array( + 'user/' . $this->admin_user->uid => $this->admin_user->name, + ); + $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $this->admin_user->name); + + // Create a second user to verify breadcrumb on user pages again. + $this->web_user = $this->drupalCreateUser(array( + 'administer users', + 'access user profiles', + )); + $this->drupalLogin($this->web_user); + + // Verify correct breadcrumb and page title on another user's account pages + // (without menu link). + $trail = $home; + $this->assertBreadcrumb('user/' . $this->admin_user->uid, $trail, $this->admin_user->name); $trail += array( 'user/' . $this->admin_user->uid => $this->admin_user->name, ); $this->assertBreadcrumb('user/' . $this->admin_user->uid . '/edit', $trail, $this->admin_user->name); + // Verify correct breadcrumb and page title when viewing own user account + // pages (without menu link). + $trail = $home; + $this->assertBreadcrumb('user/' . $this->web_user->uid, $trail, $this->web_user->name); + $trail += array( + 'user/' . $this->web_user->uid => $this->web_user->name, + ); + $this->assertBreadcrumb('user/' . $this->web_user->uid . '/edit', $trail, $this->web_user->name); + // Add a Navigation menu links for 'user' and $this->admin_user. // Although it may be faster to manage these links via low-level API // functions, there's a lot that can go wrong in doing so. + $this->drupalLogin($this->admin_user); $edit = array( 'link_title' => 'User', 'link_path' => 'user', diff --git a/modules/simpletest/tests/menu_test.info b/modules/simpletest/tests/menu_test.info index b1896e2c3dbff3652ef243194a84dc0d74e1360c..6a3dcc2d98b20c97dfb5c8178fbd0a002b33b0ac 100644 --- a/modules/simpletest/tests/menu_test.info +++ b/modules/simpletest/tests/menu_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = menu_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/menu_test.module b/modules/simpletest/tests/menu_test.module index be1233278890f78533c9eff2c51f02d232d60256..3565e119e2168a585a6d109d346e3965dd5029fe 100644 --- a/modules/simpletest/tests/menu_test.module +++ b/modules/simpletest/tests/menu_test.module @@ -1,5 +1,5 @@ <?php -// $Id: menu_test.module,v 1.17 2010/10/01 15:24:18 webchick Exp $ +// $Id: menu_test.module,v 1.18 2010/10/15 04:46:59 webchick Exp $ /** * @file @@ -227,6 +227,34 @@ function menu_test_menu() { 'access callback' => TRUE, ); + $items['menu-title-test/case1'] = array( + 'title' => 'Example title - Case 1', + 'access callback' => TRUE, + 'page callback' => 'menu_test_callback', + ); + $items['menu-title-test/case2'] = array( + 'title' => 'Example @sub1 - Case @op2', + // If '2' is not in quotes, the argument becomes arg(2). + 'title arguments' => array('@sub1' => 'title', '@op2' => '2'), + 'access callback' => TRUE, + 'page callback' => 'menu_test_callback', + ); + $items['menu-title-test/case3'] = array( + 'title' => 'Example title', + 'title callback' => 'menu_test_title_callback', + 'access callback' => TRUE, + 'page callback' => 'menu_test_callback', + ); + $items['menu-title-test/case4'] = array( + // Title gets completely ignored. Good thing, too. + 'title' => 'Bike sheds full of blue smurfs', + 'title callback' => 'menu_test_title_callback', + // If '4' is not in quotes, the argument becomes arg(4). + 'title arguments' => array('Example title', '4'), + 'access callback' => TRUE, + 'page callback' => 'menu_test_callback', + ); + return $items; } @@ -384,3 +412,15 @@ function menu_test_menu_site_status_alter(&$menu_site_status, $path) { function menu_login_callback() { return 'This is menu_login_callback().'; } + +/** + * Concatenates a string, by using the t() function and a case number. + * + * @param $title + * Title string. + * @param $case_number + * The current case number which is tests (defaults to 3). + */ +function menu_test_title_callback($title, $case_no = 3) { + return t($title) . ' - Case ' . $case_no; +} diff --git a/modules/simpletest/tests/module_test.info b/modules/simpletest/tests/module_test.info index e7325a2bea34dbe2d83e7c711f10dbe60512563e..9f7b6a2eab6708c0261ee6eb3f9f021ebf25ffc8 100644 --- a/modules/simpletest/tests/module_test.info +++ b/modules/simpletest/tests/module_test.info @@ -8,8 +8,8 @@ files[] = module_test.module files[] = module_test.file.inc hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/requirements1_test.info b/modules/simpletest/tests/requirements1_test.info index 2bcdb9aa4ce306ec105bf8a4f6130cca76b166fe..e383c1a606ab362489ec2f9a148441b6c0fa342f 100644 --- a/modules/simpletest/tests/requirements1_test.info +++ b/modules/simpletest/tests/requirements1_test.info @@ -8,8 +8,8 @@ files[] = requirements1_test.install files[] = requirements1_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/requirements2_test.info b/modules/simpletest/tests/requirements2_test.info index 1265a6ef086a908c3d48dbf987e073cd5d0d4d2a..d69a3114d177837a5f363b3c03d53e1096b5282d 100644 --- a/modules/simpletest/tests/requirements2_test.info +++ b/modules/simpletest/tests/requirements2_test.info @@ -9,8 +9,8 @@ core = 7.x files[] = requirements2_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/schema.test b/modules/simpletest/tests/schema.test index b9964a1afb2132af843f00a7d60ab50a3631235e..34731f5830274d7f0c8e6216908e56c1d79b6ad5 100644 --- a/modules/simpletest/tests/schema.test +++ b/modules/simpletest/tests/schema.test @@ -1,5 +1,5 @@ <?php -// $Id: schema.test,v 1.22 2010/09/28 02:30:32 dries Exp $ +// $Id: schema.test,v 1.23 2010/10/22 15:18:56 webchick Exp $ /** * @file @@ -112,6 +112,25 @@ class SchemaTestCase extends DrupalWebTestCase { $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField(); $this->assertEqual($count, 2, t('There were two rows.')); + + // Use database specific data type and ensure that table is created. + $table_specification = array( + 'description' => 'Schema table description.', + 'fields' => array( + 'timestamp' => array( + 'mysql_type' => 'timestamp', + 'pgsql_type' => 'timestamp', + 'sqlite_type' => 'datetime', + 'not null' => FALSE, + 'default' => NULL, + ), + ), + ); + try { + db_create_table('test_timestamp', $table_specification); + } + catch (Exception $e) {} + $this->assertTrue(db_table_exists('test_timestamp'), t('Table with database specific datatype was created.')); } function tryInsert($table = 'test_table') { diff --git a/modules/simpletest/tests/session.test b/modules/simpletest/tests/session.test index 6c07a8ce3c61e7b6c63c5c00211879bfbd682541..7c0bec088004f3349c5245747de5be0ea70238a8 100644 --- a/modules/simpletest/tests/session.test +++ b/modules/simpletest/tests/session.test @@ -1,5 +1,5 @@ <?php -// $Id: session.test,v 1.32 2010/09/01 20:08:17 dries Exp $ +// $Id: session.test,v 1.33 2010/10/15 04:15:41 webchick Exp $ /** * @file @@ -180,6 +180,48 @@ class SessionTestCase extends DrupalWebTestCase { $this->assertNoText(t('This is a dummy message.'), t('The message was not saved.')); } + /** + * Test that sessions are only saved when necessary. + */ + function testSessionWrite() { + $user = $this->drupalCreateUser(array('access content')); + $this->drupalLogin($user); + + $sql = 'SELECT u.access, s.timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.uid = :uid'; + $times1 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); + + // Before every request we sleep one second to make sure that if the session + // is saved, its timestamp will change. + + // Modify the session. + sleep(1); + $this->drupalGet('session-test/set/foo'); + $times2 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); + $this->assertEqual($times2->access, $times1->access, t('Users table was not updated.')); + $this->assertNotEqual($times2->timestamp, $times1->timestamp, t('Sessions table was updated.')); + + // Write the same value again, i.e. do not modify the session. + sleep(1); + $this->drupalGet('session-test/set/foo'); + $times3 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); + $this->assertEqual($times3->access, $times1->access, t('Users table was not updated.')); + $this->assertEqual($times3->timestamp, $times2->timestamp, t('Sessions table was not updated.')); + + // Do not change the session. + sleep(1); + $this->drupalGet(''); + $times4 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); + $this->assertEqual($times4->access, $times3->access, t('Users table was not updated.')); + $this->assertEqual($times4->timestamp, $times3->timestamp, t('Sessions table was not updated.')); + + // Force updating of users and sessions table once per second. + variable_set('session_write_interval', 0); + $this->drupalGet(''); + $times5 = db_query($sql, array(':uid' => $user->uid))->fetchObject(); + $this->assertNotEqual($times5->access, $times4->access, t('Users table was updated.')); + $this->assertNotEqual($times5->timestamp, $times4->timestamp, t('Sessions table was updated.')); + } + /** * Reset the cookie file so that it refers to the specified user. * diff --git a/modules/simpletest/tests/session_test.info b/modules/simpletest/tests/session_test.info index 167e6a175449f25e7a45f2afd6e9f2692e670410..e010c0f9fdbc9b6c765f979632390bce7c295f47 100644 --- a/modules/simpletest/tests/session_test.info +++ b/modules/simpletest/tests/session_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = session_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/system_dependencies_test.info b/modules/simpletest/tests/system_dependencies_test.info index ebbf0d527ab2aac1f0aa2e0c51139d1078770744..d2e3fe3a97fab0b2da1f0f466c226fbed3f492fe 100644 --- a/modules/simpletest/tests/system_dependencies_test.info +++ b/modules/simpletest/tests/system_dependencies_test.info @@ -8,8 +8,8 @@ files[] = system_dependencies_test.module hidden = TRUE dependencies[] = _missing_dependency -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/system_test.info b/modules/simpletest/tests/system_test.info index 0e4d11103b1b7712075913e388167b451b025d9b..9dc6081027b06d2bf31ee795b3efb0ae501dc5ac 100644 --- a/modules/simpletest/tests/system_test.info +++ b/modules/simpletest/tests/system_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = system_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/taxonomy_test.info b/modules/simpletest/tests/taxonomy_test.info index 1868421eee4f01a983700f89ee8046de6173731c..85334834d15b8e11d9d0b8983fe2ddd012cdd284 100644 --- a/modules/simpletest/tests/taxonomy_test.info +++ b/modules/simpletest/tests/taxonomy_test.info @@ -8,8 +8,8 @@ files[] = taxonomy_test.module hidden = TRUE dependencies[] = taxonomy -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/theme_test.info b/modules/simpletest/tests/theme_test.info index 3f168f3a1b4a5fa980b5fd1b82b9886a506d02d3..ddcbd56ef683850619e63b78fa2c592e3fd6913f 100644 --- a/modules/simpletest/tests/theme_test.info +++ b/modules/simpletest/tests/theme_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = theme_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/update_test_1.info b/modules/simpletest/tests/update_test_1.info index 44579a1dedf6e014c2c2b8da56e17d2e1b5c5268..9e039b5379dbac9c25d4ffee0bb22a16a82470a3 100644 --- a/modules/simpletest/tests/update_test_1.info +++ b/modules/simpletest/tests/update_test_1.info @@ -8,8 +8,8 @@ files[] = update_test_1.module files[] = update_test_1.install hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/update_test_2.info b/modules/simpletest/tests/update_test_2.info index 3955cddf3fa009a7e6af06b497a2b8a7e1d05c49..ce44fb02fe3b52d988a26ec75fceb2257b3343d5 100644 --- a/modules/simpletest/tests/update_test_2.info +++ b/modules/simpletest/tests/update_test_2.info @@ -8,8 +8,8 @@ files[] = update_test_2.module files[] = update_test_2.install hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/update_test_3.info b/modules/simpletest/tests/update_test_3.info index baeea5f94e57bed8ef3529061c43ee2616917686..6cc3ac95e7022afbe38e7f84197811157a5a05f6 100644 --- a/modules/simpletest/tests/update_test_3.info +++ b/modules/simpletest/tests/update_test_3.info @@ -8,8 +8,8 @@ files[] = update_test_3.module files[] = update_test_3.install hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test index 0c819d09d548967fbe35d2fabdcc824a13af66ee..b72e91fadc90702927a620dbaae075a8f45dc5a9 100644 --- a/modules/simpletest/tests/upgrade/upgrade.test +++ b/modules/simpletest/tests/upgrade/upgrade.test @@ -1,5 +1,5 @@ <?php -// $Id: upgrade.test,v 1.8 2010/10/01 18:37:22 webchick Exp $ +// $Id: upgrade.test,v 1.9 2010/10/08 18:19:11 webchick Exp $ /** * Perform end-to-end tests of the upgrade path. @@ -381,5 +381,9 @@ class BasicUpgradePath extends UpgradePathTestCase { $this->assertText(t('Reports')); $this->assertText(t('Structure')); $this->assertText(t('Modules')); + + // Confirm that no {menu_links} entry exists for user/autocomplete. + $result = db_query('SELECT COUNT(*) FROM {menu_links} WHERE link_path = :user_autocomplete', array(':user_autocomplete' => 'user/autocomplete'))->fetchField(); + $this->assertFalse($result, t('No {menu_links} entry exists for user/autocomplete')); } } diff --git a/modules/simpletest/tests/url_alter_test.info b/modules/simpletest/tests/url_alter_test.info index 97b453616d76caf3b1adb6963f528733141d5bb2..db9ba131a6ab52da13cdfda11c4d994d6172138a 100644 --- a/modules/simpletest/tests/url_alter_test.info +++ b/modules/simpletest/tests/url_alter_test.info @@ -8,8 +8,8 @@ files[] = url_alter_test.module files[] = url_alter_test.install hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/simpletest/tests/xmlrpc_test.info b/modules/simpletest/tests/xmlrpc_test.info index 9be23bff29d739338877a156a5aeba9f0e91b8cc..15022f5bd8b6275de1d5b617170ba58a9cb6073d 100644 --- a/modules/simpletest/tests/xmlrpc_test.info +++ b/modules/simpletest/tests/xmlrpc_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = xmlrpc_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/statistics/statistics.info b/modules/statistics/statistics.info index d7c56a67f4cc6648040c1b38ec7d4a83e7a327a3..b0147880006c0890859f54456fa6a7470c9e2321 100644 --- a/modules/statistics/statistics.info +++ b/modules/statistics/statistics.info @@ -12,8 +12,8 @@ files[] = statistics.test files[] = statistics.tokens.inc configure = admin/config/system/statistics -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/syslog/syslog.info b/modules/syslog/syslog.info index 61a704e26f36c166a1ab48870953cc06cd616382..2a92390ad0dca01f7d695d3ebd95fe7ec7f76d31 100644 --- a/modules/syslog/syslog.info +++ b/modules/syslog/syslog.info @@ -7,8 +7,8 @@ core = 7.x files[] = syslog.module files[] = syslog.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/system/system.admin-rtl.css b/modules/system/system.admin-rtl.css index dcda9bc426ba8806ae3846df800e7a7111a3db7e..bcc98e1f0f368ad29b7912712e71d9c47504e66c 100644 --- a/modules/system/system.admin-rtl.css +++ b/modules/system/system.admin-rtl.css @@ -1,4 +1,4 @@ -/* $Id: system.admin-rtl.css,v 1.2 2010/09/25 02:28:14 dries Exp $ */ +/* $Id: system.admin-rtl.css,v 1.3 2010/10/09 05:18:53 webchick Exp $ */ /** * @file @@ -32,12 +32,12 @@ div.admin .expert-link { /** * Status report. */ -table.system-status-report th, -table.system-status-report tr.merge-up td { - padding-right: 30px; +table.system-status-report td.status-icon { + padding-left: 0; + padding-right: 6px; } -table.system-status-report th { - background-position: 95% 50%; +table.system-status-report tr.merge-up td { + padding: 0 28px 8px 6px; } /** diff --git a/modules/system/system.admin.css b/modules/system/system.admin.css index ca922e2a7b38630d49d6f57d91c1123c41b897b6..80405cf22afd8411c5ccb84d3babba57f75ef5e9 100644 --- a/modules/system/system.admin.css +++ b/modules/system/system.admin.css @@ -1,4 +1,4 @@ -/* $Id: system.admin.css,v 1.2 2010/09/25 02:28:14 dries Exp $ */ +/* $Id: system.admin.css,v 1.6 2010/10/15 04:40:41 webchick Exp $ */ /** * @file @@ -45,6 +45,16 @@ div.admin .expert-link { margin: 0 0 0.5em 0; } +/** + * Quick inline admin links. + */ +small .admin-link:before { + content: '['; +} +small .admin-link:after { + content: ']'; +} + /** * Modules page. */ @@ -87,36 +97,34 @@ a.module-link-configure { /** * Status report. */ -table.system-status-report th { - border-bottom: 1px solid #ccc; +table.system-status-report td { + padding: 6px; + vertical-align: middle; } -table.system-status-report th, table.system-status-report tr.merge-up td { - padding-left: 30px; /* LTR */ + padding: 0 6px 8px 28px; /* LTR */ } -table.system-status-report th { - background-repeat: no-repeat; - background-position: 5px 50%; /* LTR */ - padding-top: 6px; - padding-bottom: 6px; +table.system-status-report td.status-icon { + width: 16px; + padding-right: 0; /* LTR */ } -table.system-status-report tr.error th { - background-image: url(../../misc/watchdog-error.png); +table.system-status-report td.status-icon div { + background-repeat: no-repeat; + height: 16px; + width: 16px; } -table.system-status-report tr.warning th { - background-image: url(../../misc/watchdog-warning.png); +table.system-status-report tr.error td.status-icon div { + background-image: url(../../misc/message-16-error.png); } -table.system-status-report tr.ok th { - background-image: url(../../misc/watchdog-ok.png); +table.system-status-report tr.warning td.status-icon div { + background-image: url(../../misc/message-16-warning.png); } tr.merge-down, -tr.merge-down td, -tr.merge-down th { +tr.merge-down td { border-bottom-width: 0 !important; } tr.merge-up, -tr.merge-up td, -tr.merge-up th { +tr.merge-up td { border-top-width: 0 !important; } diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 611081c3031154c7a128216dea2ce6677a6a98a1..108a1eb8c3a0b7667184756af6d549dc29c3ae66 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: system.admin.inc,v 1.317 2010/10/07 03:26:41 webchick Exp $ +// $Id: system.admin.inc,v 1.323 2010/10/20 16:19:24 webchick Exp $ /** * @file @@ -34,10 +34,6 @@ function system_admin_config_page() { } $block = $item; $block['content'] = ''; - if ($item['block_callback'] && function_exists($item['block_callback'])) { - $function = $item['block_callback']; - $block['content'] .= $function(); - } $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item))); if (!empty($block['content'])) { $block['show'] = TRUE; @@ -1250,6 +1246,8 @@ function system_modules_uninstall($form, $form_state = NULL) { if (!empty($options)) { $form['uninstall'] = array( '#type' => 'checkboxes', + '#title' => t('Modules'), + '#title_display' => 'invisible', '#options' => $options, ); $form['actions'] = array('#type' => 'actions'); @@ -1346,7 +1344,6 @@ function system_modules_uninstall_submit($form, &$form_state) { * use as the default value of the IP address form field. */ function system_ip_blocking($default_ip = '') { - $output = ''; $rows = array(); $header = array(t('Blocked IP addresses'), t('Operations')); $result = db_query('SELECT * FROM {blocked_ips}'); @@ -2068,28 +2065,12 @@ function system_add_date_format_type_form($form, &$form_state) { '#title' => t('Date type'), '#type' => 'textfield', '#required' => TRUE, - '#field_suffix' => ' <small id="edit-date-type-suffix"> </small>', - ); - $js_settings = array( - 'type' => 'setting', - 'data' => array( - 'machineReadableValue' => array( - 'date-type' => array( - 'text' => t('Machine name'), - 'target' => 'machine-name', - 'searchPattern' => '[^a-z0-9]+', - 'replaceToken' => '_', - ), - ), - ), ); $form['machine_name'] = array( - '#title' => t('Machine readable name'), - '#description' => t('The unique machine readable name for this date type, can only contain lowercase letters, numbers and underscores.'), - '#type' => 'textfield', - '#required' => TRUE, - '#attached' => array( - 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings), + '#type' => 'machine_name', + '#machine_name' => array( + 'exists' => 'system_get_date_types', + 'source' => array('date_type'), ), ); @@ -2492,28 +2473,38 @@ function theme_system_admin_index($variables) { */ function theme_status_report($variables) { $requirements = $variables['requirements']; - - $i = 0; + $severities = array( + REQUIREMENT_INFO => array( + 'title' => t('Info'), + 'class' => 'info', + ), + REQUIREMENT_OK => array( + 'title' => t('OK'), + 'class' => 'ok', + ), + REQUIREMENT_WARNING => array( + 'title' => t('Warning'), + 'class' => 'warning', + ), + REQUIREMENT_ERROR => array( + 'title' => t('Error'), + 'class' => 'error', + ), + ); $output = '<table class="system-status-report">'; + foreach ($requirements as $requirement) { if (empty($requirement['#type'])) { - $class = ++$i % 2 == 0 ? 'even' : 'odd'; - - $classes = array( - REQUIREMENT_INFO => 'info', - REQUIREMENT_OK => 'ok', - REQUIREMENT_WARNING => 'warning', - REQUIREMENT_ERROR => 'error', - ); - $class = $classes[isset($requirement['severity']) ? (int) $requirement['severity'] : 0] . ' ' . $class; + $severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : 0]; + $severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>'; // Output table row(s) if (!empty($requirement['description'])) { - $output .= '<tr class="' . $class . ' merge-down"><td>' . $requirement['title'] . '</td><td>' . $requirement['value'] . '</td></tr>'; - $output .= '<tr class="' . $class . ' merge-up"><td colspan="2">' . $requirement['description'] . '</td></tr>'; + $output .= '<tr class="' . $severity['class'] . ' merge-down"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>'; + $output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>'; } else { - $output .= '<tr class="' . $class . '"><td>' . $requirement['title'] . '</td><td>' . $requirement['value'] . '</td></tr>'; + $output .= '<tr class="' . $severity['class'] . '"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>'; } } } @@ -2937,7 +2928,10 @@ function system_actions_manage_form($form, &$form_state, $options = array()) { ); $form['parent']['action'] = array( '#type' => 'select', + '#title' => t('Action'), + '#title_display' => 'invisible', '#options' => $options, + '#empty_option' => t('Choose an advanced action'), ); $form['parent']['actions'] = array('#type' => 'actions'); $form['parent']['actions']['submit'] = array( diff --git a/modules/system/system.api.php b/modules/system/system.api.php index cd4cf3f4f62a51c331d10c285461c79fcdef01c8..5a8c3ecc04e7bdf9a8c462d15681a17601e9ba95 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1,5 +1,5 @@ <?php -// $Id: system.api.php,v 1.197 2010/10/02 01:22:41 dries Exp $ +// $Id: system.api.php,v 1.205 2010/10/22 17:20:41 dries Exp $ /** * @file @@ -284,6 +284,17 @@ function hook_entity_load($entities, $type) { * The type of entity being inserted (i.e. node, user, comment). */ function hook_entity_insert($entity, $type) { + // Insert the new entity into a fictional table of all entities. + $info = entity_get_info($type); + $id = reset(entity_extract_ids($type, $entity)); + db_insert('example_entity') + ->fields(array( + 'type' => $type, + 'id' => $id, + 'created' => REQUEST_TIME, + 'updated' => REQUEST_TIME, + )) + ->execute(); } /** @@ -295,6 +306,34 @@ function hook_entity_insert($entity, $type) { * The type of entity being updated (i.e. node, user, comment). */ function hook_entity_update($entity, $type) { + // Update the entity's entry in a fictional table of all entities. + $info = entity_get_info($type); + $id = reset(entity_extract_ids($type, $entity)); + db_update('example_entity') + ->fields(array( + 'updated' => REQUEST_TIME, + )) + ->condition('type', $type) + ->condition('id', $id) + ->execute(); +} + +/** + * Act on entities when deleted. + * + * @param $entity + * The entity object. + * @param $type + * The type of entity being deleted (i.e. node, user, comment). + */ +function hook_entity_delete($entity, $type) { + // Delete the entity's entry from a fictional table of all entities. + $info = entity_get_info($type); + $id = reset(entity_extract_ids($type, $entity)); + db_delete('example_entity') + ->condition('type', $type) + ->condition('id', $id) + ->execute(); } /** @@ -487,7 +526,7 @@ function hook_cron_queue_info_alter(&$queues) { * a matching theme function, e.g. theme_elementtype(), which should be * registered with hook_theme() as normal. * - * Form more information about custom element types see the explanation at + * For more information about custom element types see the explanation at * http://drupal.org/node/169815. * * @return @@ -969,9 +1008,6 @@ function hook_page_build(&$page) { * default parent for 'admin/people/create' is 'admin/people'). * - "tab_root": For local task menu items, the path of the closest non-tab * item; same default as "tab_parent". - * - "block callback": Name of a function used to render the block on the - * system administration page for this item (called with no arguments). - * If not provided, system_admin_menu_block() is used to generate it. * - "position": Position of the block ('left' or 'right') on the system * administration page for this item. * - "type": A bitmask of flags describing properties of the menu item. @@ -1365,7 +1401,9 @@ function hook_page_alter(&$page) { * @param $form * Nested array of form elements that comprise the form. * @param $form_state - * A keyed array containing the current state of the form. + * A keyed array containing the current state of the form. The arguments + * that drupal_get_form() was originally called with are available in the + * array $form_state['build_info']['args']. * @param $form_id * String representing the name of the form itself. Typically this is the * name of the function that generated the form. @@ -1393,7 +1431,9 @@ function hook_form_alter(&$form, &$form_state, $form_id) { * @param $form * Nested array of form elements that comprise the form. * @param $form_state - * A keyed array containing the current state of the form. + * A keyed array containing the current state of the form. The arguments + * that drupal_get_form() was originally called with are available in the + * array $form_state['build_info']['args']. * @param $form_id * String representing the name of the form itself. Typically this is the * name of the function that generated the form. @@ -2257,9 +2297,13 @@ function hook_modules_uninstalled($modules) { * - 'class' A string specifying the PHP class that implements the * DrupalStreamWrapperInterface interface. * - 'description' A string with a short description of what the wrapper does. - * - 'type' A bitmask of flags indicating what type of streams this wrapper - * will access - local or remote, readable and/or writeable, etc. Many - * shortcut constants are defined in stream_wrappers.inc. + * - 'type' (Optional) A bitmask of flags indicating what type of streams this + * wrapper will access - local or remote, readable and/or writeable, etc. + * Many shortcut constants are defined in stream_wrappers.inc. Defaults to + * STREAM_WRAPPERS_NORMAL which includes all of these bit flags: + * - STREAM_WRAPPERS_READ + * - STREAM_WRAPPERS_WRITE + * - STREAM_WRAPPERS_VISIBLE * * @see file_get_stream_wrappers() * @see hook_stream_wrappers_alter() @@ -2271,18 +2315,35 @@ function hook_stream_wrappers() { 'name' => t('Public files'), 'class' => 'DrupalPublicStreamWrapper', 'description' => t('Public local files served by the webserver.'), + 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, ), 'private' => array( 'name' => t('Private files'), 'class' => 'DrupalPrivateStreamWrapper', 'description' => t('Private local files served by Drupal.'), + 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, ), 'temp' => array( 'name' => t('Temporary files'), 'class' => 'DrupalTempStreamWrapper', 'description' => t('Temporary local files for upload and previews.'), - 'type' => STREAM_WRAPPERS_HIDDEN, - ) + 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, + ), + 'cdn' => array( + 'name' => t('Content delivery network files'), + 'class' => 'MyModuleCDNStreamWrapper', + 'description' => t('Files served by a content delivery network.'), + // 'type' can be omitted to use the default of STREAM_WRAPPERS_NORMAL + ), + 'youtube' => array( + 'name' => t('YouTube video'), + 'class' => 'MyModuleYouTubeStreamWrapper', + 'description' => t('Video streamed from YouTube.'), + // A module implementing YouTube integration may decide to support using + // the YouTube API for uploading video, but here, we assume that this + // particular module only supports playing YouTube video. + 'type' => STREAM_WRAPPERS_READ_VISIBLE, + ), ); } @@ -2871,7 +2932,7 @@ function hook_install() { * to provide feedback regarding completion level. * * See the batch operations page for more information on how to use the batch API: - * @link http://drupal.org/node/146843 http://drupal.org/node/146843 @endlink + * @link http://drupal.org/node/180528 http://drupal.org/node/180528 @endlink * * @throws DrupalUpdateException, PDOException * In case of error, update hooks should throw an instance of DrupalUpdateException @@ -3748,26 +3809,36 @@ function hook_username_alter(&$name, $account) { /** * Provide replacement values for placeholder tokens. * + * This hook is invoked when someone calls token_replace(). That function first + * scans the text for [type:token] patterns, and splits the needed tokens into + * groups by type. Then hook_tokens() is invoked on each token-type group, + * allowing your module to respond by providing replacement text for any of + * the tokens in the group that your module knows how to process. + * + * A module implementing this hook should also implement hook_token_info() in + * order to list its available tokens on editing screens. + * * @param $type - * The type of token being replaced. 'node', 'user', and 'date' are common. + * The machine-readable name of the type (group) of token being replaced, such + * as 'node', 'user', or another type defined by a hook_token_info() + * implementation. * @param $tokens - * An array of tokens to be replaced, keyed by the literal text of the token - * as it appeared in the source text. + * An array of tokens to be replaced. The keys are the machine-readable token + * names, and the values are the raw [type:token] strings that appeared in the + * original text. * @param $data - * (optional) An associative array of objects to be used when generating replacement - * values. + * (optional) An associative array of data objects to be used when generating + * replacement values, as supplied in the $data parameter to token_replace(). * @param $options - * (optional) A associative array of options to control the token - * replacement process. Common options are: - * - 'language' A language object to be used when generating locale-sensitive - * tokens. - * - 'sanitize' A boolean flag indicating that tokens should be sanitized for - * display to a web browser. + * (optional) An associative array of options for token replacement; see + * token_replace() for possible values. * * @return - * An associative array of replacement values, keyed by the original 'raw' - * tokens that were found in the source text. For example: - * $results['[node:title]'] = 'My new node'; + * An associative array of replacement values, keyed by the raw [type:token] + * strings from the original text. + * + * @see hook_token_info() + * @see hook_tokens_alter() */ function hook_tokens($type, $tokens, array $data = array(), array $options = array()) { $url_options = array('absolute' => TRUE); @@ -3826,14 +3897,88 @@ function hook_tokens($type, $tokens, array $data = array(), array $options = arr } /** - * Provide metadata about available placeholder tokens and token types. + * Alter replacement values for placeholder tokens. + * + * @param $replacements + * An associative array of replacements returned by hook_tokens(). + * @param $context + * The context in which hook_tokens() was called. An associative array with + * the following keys, which have the same meaning as the corresponding + * parameters of hook_tokens(): + * - 'type' + * - 'tokens' + * - 'data' + * - 'options' + * + * @see hook_tokens() + */ +function hook_tokens_alter(array &$replacements, array $context) { + $options = $context['options']; + + if (isset($options['language'])) { + $url_options['language'] = $options['language']; + $language_code = $options['language']->language; + } + else { + $language_code = NULL; + } + $sanitize = !empty($options['sanitize']); + + if ($context['type'] == 'node' && !empty($context['data']['node'])) { + $node = $context['data']['node']; + + // Alter the [node:title] token, and replace it with the rendered content + // of a field (field_title). + if (isset($context['tokens']['title'])) { + $title = field_view_field('node', $node, 'field_title', 'default', $language_code); + $replacements[$context['tokens']['title']] = drupal_render($title); + } + } +} + +/** + * Provide information about available placeholder tokens and token types. + * + * Tokens are placeholders that can be put into text by using the syntax + * [type:token], where type is the machine-readable name of a token type, and + * token is the machine-readable name of a token within this group. This hook + * provides a list of types and tokens to be displayed on text editing screens, + * so that people editing text can see what their token options are. + * + * The actual token replacement is done by token_replace(), which invokes + * hook_tokens(). Your module will need to implement that hook in order to + * generate token replacements from the tokens defined here. * * @return - * An associative array of available tokens and token types, each containing - * the raw name of the token or type, its user-friendly name, and a verbose - * description. + * An associative array of available tokens and token types. The outer array + * has two components: + * - types: An associative array of token types (groups). Each token type is + * an associative array with the following components: + * - name: The translated human-readable short name of the token type. + * - description: A translated longer description of the token type. + * - needs-data: The type of data that must be provided to token_replace() + * in the $data argument (i.e., the key name in $data) in order for tokens + * of this type to be used in the $text being processed. For instance, if + * the token needs a node object, 'needs-data' should be 'node', and to + * use this token in token_replace(), the caller needs to supply a node + * object as $data['node']. Some token data can also be supplied + * indirectly; for instance, a node object in $data supplies a user object + * (the author of the node), allowing user tokens to be used when only + * a node data object is supplied. + * - tokens: An associative array of tokens. The outer array is keyed by the + * group name (the same key as in the types array). Within each group of + * tokens, each token item is keyed by the machine name of the token, and + * each token item has the following components: + * - name: The translated human-readable short name of the token. + * - description: A translated longer description of the token. + * - type (optional): A 'needs-data' data type supplied by this token, which + * should match a 'needs-data' value from another token type. For example, + * the node author token provides a user object, which can then be used + * for token replacement data in token_replace() without having to supply + * a separate user object. * * @see hook_token_info_alter() + * @see hook_tokens() */ function hook_token_info() { $type = array( @@ -3874,34 +4019,6 @@ function hook_token_info() { ); } -/** - * Alter batch information before a batch is processed. - * - * Called by batch_process() to allow modules to alter a batch before it is - * processed. - * - * @param $batch - * The associative array of batch information. See batch_set() for details on - * what this could contain. - * - * @see batch_set() - * @see batch_process() - * - * @ingroup batch - */ -function hook_batch_alter(&$batch) { - // If the current page request is inside the overlay, add ?render=overlay to - // the success callback URL, so that it appears correctly within the overlay. - if (overlay_get_mode() == 'child') { - if (isset($batch['url_options']['query'])) { - $batch['url_options']['query']['render'] = 'overlay'; - } - else { - $batch['url_options']['query'] = array('render' => 'overlay'); - } - } -} - /** * Alter the metadata about available placeholder tokens and token types. * @@ -3929,6 +4046,33 @@ function hook_token_info_alter(&$data) { ); } +/** + * Alter batch information before a batch is processed. + * + * Called by batch_process() to allow modules to alter a batch before it is + * processed. + * + * @param $batch + * The associative array of batch information. See batch_set() for details on + * what this could contain. + * + * @see batch_set() + * @see batch_process() + * + * @ingroup batch + */ +function hook_batch_alter(&$batch) { + // If the current page request is inside the overlay, add ?render=overlay to + // the success callback URL, so that it appears correctly within the overlay. + if (overlay_get_mode() == 'child') { + if (isset($batch['url_options']['query'])) { + $batch['url_options']['query']['render'] = 'overlay'; + } + else { + $batch['url_options']['query'] = array('render' => 'overlay'); + } + } +} /** * Provide information on Updaters (classes that can update Drupal). diff --git a/modules/system/system.info b/modules/system/system.info index f1d33538d61f4bd6c71465d25caebc204bcfd04e..2892740ad34d83a461ced6d8d99de38bf66e118b 100644 --- a/modules/system/system.info +++ b/modules/system/system.info @@ -18,8 +18,8 @@ files[] = system.mail.inc required = TRUE configure = admin/config/system -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/system/system.install b/modules/system/system.install index e3ceaef2d9db35203bbf72648d1ef0c919647e89..0a5665dc90746ada304740b35d4444815ef30efa 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -1,5 +1,5 @@ <?php -// $Id: system.install,v 1.515 2010/10/06 13:38:40 dries Exp $ +// $Id: system.install,v 1.520 2010/10/20 00:47:44 dries Exp $ /** * @file @@ -77,6 +77,15 @@ function system_requirements($phase) { // If PHP is old, it's not safe to continue with the requirements check. return $requirements; } + // Check that htmlspecialchars() is secure if the site is running any PHP + // version older than 5.2.5. We don't simply require 5.2.5, because Ubuntu + // 8.04 ships with PHP 5.2.4, but includes the necessary security patch. + elseif (version_compare($phpversion, '5.2.5') < 0 && strlen(@htmlspecialchars(chr(0xC0) . chr(0xAF), ENT_QUOTES, 'UTF-8'))) { + $requirements['php']['description'] = $t('Your PHP installation is too old. Drupal requires at least PHP 5.2.5, or PHP @version with the htmlspecialchars security patch backported.', array('@version' => DRUPAL_MINIMUM_PHP)); + $requirements['php']['severity'] = REQUIREMENT_ERROR; + // If PHP is old, it's not safe to continue with the requirements check. + return $requirements; + } // Test PHP register_globals setting. $requirements['php_register_globals'] = array( @@ -144,19 +153,36 @@ function system_requirements($phase) { 'title' => $t('Database support'), ); - // Test for at least one suitable PDO extension, if PDO is available. + // Make sure PDO is available. $database_ok = extension_loaded('pdo'); - if ($database_ok) { + if (!$database_ok) { + $pdo_message = $t('Your web server does not appear to support PDO (PHP Data Objects). Ask your hosting provider if they support the native PDO extension. See the <a href="@link">system requirements</a> page for more information.', array( + '@link' => 'http://drupal.org/requirements/pdo', + )); + } + else { + // Make sure at least one supported database driver exists. $drivers = drupal_detect_database_types(); - $database_ok = !empty($drivers); + if (empty($drivers)) { + $database_ok = FALSE; + $pdo_message = $t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that <a href="@drupal-databases">Drupal supports</a>.', array( + '@drupal-databases' => 'http://drupal.org/node/270#database', + )); + } + // Make sure the native PDO extension is available, not the older PEAR + // version. (See install_verify_pdo() for details.) + if (!defined('PDO::ATTR_DEFAULT_FETCH_MODE')) { + $database_ok = FALSE; + $pdo_message = $t('Your web server seems to have the wrong version of PDO installed. Drupal 7 requires the PDO extension from PHP core. This system has the older PECL version. See the <a href="@link">system requirements</a> page for more information.', array( + '@link' => 'http://drupal.org/requirements/pdo#pecl', + )); + } } if (!$database_ok) { $requirements['database_extensions']['value'] = $t('Disabled'); $requirements['database_extensions']['severity'] = REQUIREMENT_ERROR; - $requirements['database_extensions']['description'] = $t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that <a href="@drupal-databases">Drupal supports</a>.', array( - '@drupal-databases' => 'http://drupal.org/node/270#database', - )); + $requirements['database_extensions']['description'] = $pdo_message; } else { $requirements['database_extensions']['value'] = $t('Enabled'); @@ -1066,13 +1092,6 @@ function system_schema() { 'not null' => TRUE, 'default' => 0, ), - 'block_callback' => array( - 'description' => 'Name of a function used to render the block on the system administration page for this item.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), 'description' => array( 'description' => 'A description of this item.', 'type' => 'text', @@ -1978,11 +1997,23 @@ function system_update_7015() { ->fields(array('link_path' => 'user/logout')) ->condition('link_path', 'logout') ->execute(); - - db_update('menu_links') + db_update('menu_links') ->fields(array('router_path' => 'user/logout')) ->condition('router_path', 'logout') ->execute(); + + db_update('menu_links') + ->fields(array( + 'menu_name' => 'user-menu', + 'plid' => 0, + )) + ->condition(db_or() + ->condition('link_path', 'user/logout') + ->condition('router_path', 'user/logout') + ) + ->condition('module', 'system') + ->condition('customized', 0) + ->execute(); } /** @@ -2850,6 +2881,26 @@ function system_update_7062() { db_add_index('system', 'system_list', array('status', 'bootstrap', 'type', 'weight', 'name')); } +/** + * Delete {menu_links} records for 'type' => MENU_CALLBACK which would not appear in a fresh install. + */ +function system_update_7063() { + // For router items where 'type' => MENU_CALLBACK, {menu_router}.type is + // stored as 4 in Drupal 6, and 0 in Drupal 7. Fortunately Drupal 7 doesn't + // store any types as 4, so delete both. + $result = db_query('SELECT ml.mlid FROM {menu_links} ml INNER JOIN {menu_router} mr ON ml.router_path = mr.path WHERE ml.module = :system AND ml.customized = 0 AND mr.type IN(:callbacks)', array(':callbacks' => array(0, 4), ':system' => 'system')); + foreach ($result as $record) { + db_delete('menu_links')->condition('mlid', $record->mlid)->execute(); + } +} + +/** + * Remove block_callback field from {menu_router}. + */ +function system_update_7064() { + db_drop_field('menu_router', 'block_callback'); +} + /** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/system/system.js b/modules/system/system.js index b27b210ed417c4fd34c39c66263663338c559c82..09294932aab7004342a02c9ae740e41483467f8a 100644 --- a/modules/system/system.js +++ b/modules/system/system.js @@ -1,4 +1,4 @@ -// $Id: system.js,v 1.40 2010/01/30 00:29:10 webchick Exp $ +// $Id: system.js,v 1.41 2010/10/13 13:43:21 dries Exp $ (function ($) { /** @@ -148,78 +148,4 @@ Drupal.behaviors.pageCache = { } }; -/** - * Attach the auto machine readable name behavior. - * - * Settings are expected to be an object of elements to process, where the key - * defines the source element in the form and the value is an object defining - * the following properties: - * - text: The label to display before the auto-generated value. - * - target: The target form element name. - * - searchPattern: A regular expression (without modifiers) matching disallowed - * characters in the machine readable name, f.e. '[^a-z0-9]+'. - * - replaceToken: A replacement string to replace disallowed characters, f.e. - * '-' or '_'. - * - * @see menu_edit_menu() - */ -Drupal.behaviors.machineReadableValue = { - attach: function () { - var self = this; - - for (var value in Drupal.settings.machineReadableValue) { - var settings = Drupal.settings.machineReadableValue[value]; - - // Build selector for the source name entered by a user. - var source = '#edit-' + value; - var suffix = source + '-suffix'; - // Build selector for the machine readable name. - var target = '#edit-' + settings.target; - // Build selector for the wrapper element around the target field. - var wrapper = '.form-item-' + settings.target; - - // Do not process the element if we got an error or the given name and the - // machine readable name are identical or the machine readable name is - // empty. - if (!$(target).hasClass('error') && ($(target).val() == '' || $(target).val() == self.transliterate($(source).val(), settings))) { - // Hide wrapper element. - $(wrapper).hide(); - // Bind keyup event to source element. - $(source).keyup(function () { - var machine = self.transliterate($(this).val(), settings); - if (machine != '_' && machine != '') { - // Set machine readable name to the user entered value. - $(target).val(machine); - // Append the machine readable name and a link to edit it to the source field. - $(suffix).empty().append(' ' + settings.text + ': ' + machine + ' [').append($('<a href="#">' + Drupal.t('Edit') + '</a>').click(function () { - $(wrapper).show(); - $(target).focus(); - $(suffix).hide(); - $(source).unbind('keyup'); - return false; - })).append(']'); - } - else { - $(target).val(machine); - $(suffix).text(''); - } - }); - // Call keyup event on source element. - $(source).keyup(); - } - } - }, - - /** - * Transliterate a human-readable name to a machine name. - * - * The result should not contain any character matching settings.searchPattern, - * invalid characters are typically replaced with settings.replaceToken. - */ - transliterate: function (source, settings) { - var searchPattern = new RegExp(settings.searchPattern, 'g'); - return source.toLowerCase().replace(searchPattern, settings.replaceToken); - } -}; - })(jQuery); diff --git a/modules/system/system.module b/modules/system/system.module index 78774d1d67f884d8fff75491f5ead6882bc06db0..0f219253a4fd9aa563734ed62973f1160e2cf8a8 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -1,5 +1,5 @@ <?php -// $Id: system.module,v 1.976 2010/10/05 19:59:10 dries Exp $ +// $Id: system.module,v 1.983 2010/10/21 12:09:41 dries Exp $ /** * @file @@ -251,6 +251,10 @@ function system_hook_info() { $hooks['tokens'] = array( 'group' => 'tokens', ); + $hooks['tokens_alter'] = array( + 'group' => 'tokens', + ); + return $hooks; } @@ -347,6 +351,18 @@ function system_element_info() { '#theme' => 'textfield', '#theme_wrappers' => array('form_element'), ); + $types['machine_name'] = array( + '#input' => TRUE, + '#default_value' => NULL, + '#required' => TRUE, + '#maxlength' => 64, + '#size' => 60, + '#autocomplete_path' => FALSE, + '#process' => array('form_process_machine_name', 'ajax_process_form'), + '#element_validate' => array('form_validate_machine_name'), + '#theme' => 'textfield', + '#theme_wrappers' => array('form_element'), + ); $types['password'] = array( '#input' => TRUE, '#size' => 60, @@ -1529,12 +1545,13 @@ function system_stream_wrappers() { 'name' => t('Public files'), 'class' => 'DrupalPublicStreamWrapper', 'description' => t('Public local files served by the webserver.'), + 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, ), 'temporary' => array( 'name' => t('Temporary files'), 'class' => 'DrupalTemporaryStreamWrapper', 'description' => t('Temporary local files for upload and previews.'), - 'type' => STREAM_WRAPPERS_HIDDEN, + 'type' => STREAM_WRAPPERS_LOCAL_HIDDEN, ), ); @@ -1544,6 +1561,7 @@ function system_stream_wrappers() { 'name' => t('Private files'), 'class' => 'DrupalPrivateStreamWrapper', 'description' => t('Private local files served by Drupal.'), + 'type' => STREAM_WRAPPERS_LOCAL_NORMAL, ); } @@ -2184,9 +2202,11 @@ function system_update_files_database(&$files, $type) { } if (count($delete) > 0) { - // Delete all missing files from the system table + // Delete all missing files from the system table, but only if the plugin + // has never been installed. db_delete('system') ->condition($delete) + ->condition('schema_version', -1) ->execute(); } @@ -2841,6 +2861,7 @@ function system_get_module_admin_tasks($module, $info) { } $admin_tasks = array(); + $titles = array(); if ($menu = module_invoke($module, 'menu')) { foreach ($menu as $path => $item) { if (isset($links[$path])) { @@ -2851,6 +2872,25 @@ function system_get_module_admin_tasks($module, $info) { $task['description'] = $task['localized_options']['attributes']['title']; unset($task['localized_options']['attributes']['title']); } + + // Check the admin tasks for duplicate names. If one is found, + // append the parent menu item's title to differentiate. + $duplicate_path = array_search($task['title'], $titles); + if ($duplicate_path !== FALSE) { + if ($parent = menu_link_load($task['plid'])) { + // Append the parent item's title to this task's title. + $task['title'] = t('@original_title (@parent_title)', array('@original_title' => $task['title'], '@parent_title' => $parent['title'])); + } + if ($parent = menu_link_load($admin_tasks[$duplicate_path]['plid'])) { + // Append the parent item's title to the duplicated task's title. + // We use $links[$duplicate_path] in case there are triplicates. + $admin_tasks[$duplicate_path]['title'] = t('@original_title (@parent_title)', array('@original_title' => $links[$duplicate_path]['title'], '@parent_title' => $parent['title'])); + } + } + else { + $titles[$path] = $task['title']; + } + $admin_tasks[$path] = $task; } } diff --git a/modules/system/system.test b/modules/system/system.test index a4ded7e780737e193911a17fddfddfe825b7878b..74b8109c9153f961ed3bb99ca1aceb099246b029 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -1,5 +1,5 @@ <?php -// $Id: system.test,v 1.146 2010/10/05 00:22:24 webchick Exp $ +// $Id: system.test,v 1.149 2010/10/16 00:00:17 webchick Exp $ /** * Helper class for module test cases. @@ -161,11 +161,14 @@ class EnableDisableTestCase extends ModuleTestCase { /** * Tests entity cache after enabling a module with a dependency on an enitity * providing module. + * + * @see entity_cache_test_watchdog() */ function testEntityCache() { module_enable(array('entity_cache_test')); $info = variable_get('entity_cache_test'); - $this->assertNotNull($info, t('Entity information must not be NULL')); + $this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.'); + $this->assertEqual($info['controller class'], 'DrupalDefaultEntityController', 'Entity controller class info is correct.'); } } @@ -948,7 +951,7 @@ class DateTimeFunctionalTest extends DrupalWebTestCase { // Add custom date type. $this->clickLink(t('Add date type')); - $date_type = $this->randomName(8); + $date_type = strtolower($this->randomName(8)); $machine_name = 'machine_' . $date_type; $date_format = 'd.m.Y - H:i'; $edit = array( @@ -1939,7 +1942,7 @@ class ShutdownFunctionsTest extends DrupalWebTestCase { // Make sure exceptions displayed through _drupal_render_exception_safe() // are correctly escaped. - $this->assertText('Drupal is <blink>awesome</blink>.'); + $this->assertRaw('Drupal is &lt;blink&gt;awesome&lt;/blink&gt;.'); } } diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc index a509c9a19947362993d2277f4570769fa2e8c0da..e81eebb97abb41c87d91354f75a5abcdce685ab2 100644 --- a/modules/taxonomy/taxonomy.admin.inc +++ b/modules/taxonomy/taxonomy.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.admin.inc,v 1.111 2010/10/06 13:38:40 dries Exp $ +// $Id: taxonomy.admin.inc,v 1.114 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -19,7 +19,13 @@ function taxonomy_overview_vocabularies($form) { foreach ($vocabularies as $vocabulary) { $form[$vocabulary->vid]['#vocabulary'] = $vocabulary; $form[$vocabulary->vid]['name'] = array('#markup' => check_plain($vocabulary->name)); - $form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta' => 10, '#default_value' => $vocabulary->weight); + $form[$vocabulary->vid]['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight for @title', array('@title' => $vocabulary->name)), + '#title_display' => 'invisible', + '#delta' => 10, + '#default_value' => $vocabulary->weight, + ); $form[$vocabulary->vid]['edit'] = array('#type' => 'link', '#title' => t('edit vocabulary'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name/edit"); $form[$vocabulary->vid]['list'] = array('#type' => 'link', '#title' => t('list terms'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name"); $form[$vocabulary->vid]['add'] = array('#type' => 'link', '#title' => t('add terms'), '#href' => "admin/structure/taxonomy/$vocabulary->machine_name/add"); @@ -137,32 +143,19 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) { '#default_value' => $vocabulary->name, '#maxlength' => 255, '#required' => TRUE, - '#field_suffix' => ' <small id="edit-name-suffix"> </small>', - ); - $js_settings = array( - 'type' => 'setting', - 'data' => array( - 'machineReadableValue' => array( - 'name' => array( - 'text' => t('Machine name'), - 'target' => 'machine-name', - 'searchPattern' => '[^a-z0-9]+', - 'replaceToken' => '_', - ), - ), - ), ); $form['machine_name'] = array( - '#type' => 'textfield', - '#title' => t('Machine-readable name'), + '#type' => 'machine_name', '#default_value' => $vocabulary->machine_name, - '#maxlength' => 255, - '#description' => t('The unique machine-readable name for this vocabulary, used for theme templates. Can only contain lowercase letters, numbers, and underscores.'), - '#required' => TRUE, - '#attached' => array( - 'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings), + '#maxlength' => 21, + '#machine_name' => array( + 'exists' => 'taxonomy_vocabulary_machine_name_load', ), ); + $form['old_machine_name'] = array( + '#type' => 'value', + '#value' => $vocabulary->machine_name, + ); $form['description'] = array( '#type' => 'textfield', '#title' => t('Description'), @@ -185,34 +178,6 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) { return $form; } -/** - * Validation handler for the vocabulary form. - * - * @see taxonomy_form_vocabulary() - */ -function taxonomy_form_vocabulary_validate($form, &$form_state) { - if ($form_state['clicked_button']['#value'] != t('Delete') && isset($form_state['values']['machine_name'])) { - - // Restrict machine names to appropriate characters. - $machine_name = $form_state['values']['machine_name']; - if (!preg_match('!^[a-z0-9_]+$!', $form_state['values']['machine_name'])) { - form_set_error('machine_name', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.')); - } - // Restrict machine names to 21 characters to avoid exceeding the limit - // for field names. - if (drupal_strlen($machine_name) > 21) { - form_set_error('machine_name', t('The machine-readable name must not exceed 21 characters.')); - } - - // Do not allow duplicate machine names. - $vocabularies = taxonomy_get_vocabularies(); - foreach ($vocabularies as $vocabulary) { - if ($machine_name == $vocabulary->machine_name && (!isset($form_state['values']['vid']) || $vocabulary->vid != $form_state['values']['vid'])) { - form_set_error('machine_name', t('This machine-readable name is already in use by another vocabulary and must be unique.')); - } - } - } -} /** * Accept the form submission for a vocabulary and save the results. */ @@ -223,12 +188,10 @@ function taxonomy_form_vocabulary_submit($form, &$form_state) { $form_state['confirm_delete'] = TRUE; return; } - $old_machine_name = $form_state['vocabulary']->machine_name; + $vocabulary = $form_state['vocabulary']; entity_form_submit_build_entity('taxonomy_vocabulary', $vocabulary, $form, $form_state); - if ($vocabulary->machine_name != $old_machine_name) { - field_attach_rename_bundle('taxonomy_term', $old_machine_name, $vocabulary->machine_name); - } + switch (taxonomy_vocabulary_save($vocabulary)) { case SAVED_NEW: drupal_set_message(t('Created new vocabulary %name.', array('%name' => $vocabulary->name))); @@ -706,8 +669,7 @@ function taxonomy_form_term($form, &$form_state, $edit = array(), $vocabulary = ); $form['vocabulary_machine_name'] = array( - '#type' => 'textfield', - '#access' => FALSE, + '#type' => 'value', '#value' => isset($term->vocabulary_machine_name) ? $term->vocabulary_machine_name : $vocabulary->name, ); diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info index cb37bc727ab2e39cd1a1ab600b666c7366585ffc..adbbcaec69aa9331cccaf4785cb98ffc245e1d46 100644 --- a/modules/taxonomy/taxonomy.info +++ b/modules/taxonomy/taxonomy.info @@ -12,8 +12,8 @@ files[] = taxonomy.test files[] = taxonomy.tokens.inc configure = admin/structure/taxonomy -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/taxonomy/taxonomy.install b/modules/taxonomy/taxonomy.install index f111ab7afe94f31ec5deee48fa2d9945a19db8ba..5624b196f8c26b8912006710f9d36c2e942067e4 100644 --- a/modules/taxonomy/taxonomy.install +++ b/modules/taxonomy/taxonomy.install @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.install,v 1.51 2010/10/06 21:53:41 webchick Exp $ +// $Id: taxonomy.install,v 1.53 2010/10/20 15:57:42 webchick Exp $ /** * @file @@ -51,8 +51,8 @@ function taxonomy_schema() { 'translatable' => TRUE, ), 'format' => array( - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 255, 'not null' => FALSE, 'description' => 'The {filter_format}.format of the description.', ), @@ -258,6 +258,11 @@ function taxonomy_update_dependencies() { $dependencies['node'][7006] = array( 'taxonomy' => 7002, ); + // Ensure that format columns are only changed after Filter module has changed + // the primary records. + $dependencies['taxonomy'][7009] = array( + 'filter' => 7010, + ); return $dependencies; } @@ -782,3 +787,16 @@ function taxonomy_update_7008() { ), )); } + +/** + * Change {taxonomy_term_data}.format into varchar. + */ +function taxonomy_update_7009() { + db_change_field('taxonomy_term_data', 'format', 'format', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'The {filter_format}.format of the description.', + )); +} + diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index de8ed2d88f95b95ad73d7f4df9e3f7ec75e26932..a0f0ef73f46c4220daccb758457a460749cae030 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.module,v 1.609 2010/10/03 01:15:33 dries Exp $ +// $Id: taxonomy.module,v 1.613 2010/10/15 05:25:32 webchick Exp $ /** * @file @@ -374,11 +374,14 @@ function taxonomy_admin_vocabulary_title_callback($vocabulary) { * Save a vocabulary given a vocabulary object. */ function taxonomy_vocabulary_save($vocabulary) { - + // Prevent leading and trailing spaces in vocabulary names. if (!empty($vocabulary->name)) { - // Prevent leading and trailing spaces in vocabulary names. $vocabulary->name = trim($vocabulary->name); } + // For existing vocabularies, make sure we can detect machine name changes. + if (!empty($vocabulary->vid) && !isset($vocabulary->old_machine_name)) { + $vocabulary->old_machine_name = db_query("SELECT machine_name FROM {taxonomy_vocabulary} WHERE vid = :vid", array(':vid' => $vocabulary->vid))->fetchField(); + } if (!isset($vocabulary->module)) { $vocabulary->module = 'taxonomy'; @@ -388,6 +391,9 @@ function taxonomy_vocabulary_save($vocabulary) { if (!empty($vocabulary->vid) && !empty($vocabulary->name)) { $status = drupal_write_record('taxonomy_vocabulary', $vocabulary, 'vid'); + if ($vocabulary->old_machine_name != $vocabulary->machine_name) { + field_attach_rename_bundle('taxonomy_term', $vocabulary->old_machine_name, $vocabulary->machine_name); + } module_invoke_all('taxonomy_vocabulary_update', $vocabulary); module_invoke_all('entity_update', $vocabulary, 'taxonomy_vocabulary'); } @@ -425,7 +431,8 @@ function taxonomy_vocabulary_delete($vid) { ->execute(); field_attach_delete_bundle('taxonomy_term', $vocabulary['machine_name']); - module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary); + module_invoke_all('taxonomy_vocabulary_delete', $vocabulary); + module_invoke_all('entity_delete', $vocabulary, 'taxonomy_vocabulary'); cache_clear_all(); entity_get_controller('taxonomy_vocabulary')->resetCache(); @@ -433,6 +440,31 @@ function taxonomy_vocabulary_delete($vid) { return SAVED_DELETED; } +/** + * Implements hook_taxonomy_vocabulary_update(). + */ +function taxonomy_taxonomy_vocabulary_update($vocabulary) { + // Reflect machine name changes in the definitions of existing 'taxonomy' + // fields. + if (!empty($vocabulary->old_machine_name) && $vocabulary->old_machine_name != $vocabulary->machine_name) { + $fields = field_read_fields(); + foreach ($fields as $field_name => $field) { + $update = FALSE; + if ($field['type'] == 'taxonomy_term_reference') { + foreach ($field['settings']['allowed_values'] as $key => &$value) { + if ($value['vocabulary'] == $vocabulary->old_machine_name) { + $value['vocabulary'] = $vocabulary->machine_name; + $update = TRUE; + } + } + if ($update) { + field_update_field($field); + } + } + } + } +} + /** * Dynamically check and update the hierarchy flag of a vocabulary. * @@ -581,6 +613,7 @@ function taxonomy_term_delete($tid) { field_attach_delete('taxonomy_term', $term); module_invoke_all('taxonomy_term_delete', $term); + module_invoke_all('entity_delete', $term, 'taxonomy_term'); taxonomy_terms_static_reset(); } } @@ -810,21 +843,23 @@ function taxonomy_get_children($tid, $vid = 0) { * for the entire vocabulary. * @param $max_depth * The number of levels of the tree to return. Leave NULL to return all levels. - * @param $depth - * Internal use only. + * @param $load_entities + * If TRUE, a full entity load will occur on the term objects. Otherwise they + * are partial objects queried directly from the {taxonomy_term_data} table to + * save execution time and memory consumption when listing large numbers of + * terms. Defaults to FALSE. * * @return * An array of all term objects in the tree. Each term object is extended * to have "depth" and "parents" attributes in addition to its normal ones. - * Results are statically cached. + * Results are statically cached. Term objects will be partial or complete + * depending on the $load_entities parameter. */ -function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $depth = -1) { +function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) { $children = &drupal_static(__FUNCTION__, array()); $parents = &drupal_static(__FUNCTION__ . ':parents', array()); $terms = &drupal_static(__FUNCTION__ . ':terms', array()); - $depth++; - // We cache trees, so it's not CPU-intensive to call get_tree() on a term // and its children, too. if (!isset($children[$vid])) { @@ -834,31 +869,81 @@ function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $depth = -1) { $query = db_select('taxonomy_term_data', 't'); $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid'); - $query->addField('t', 'tid'); - $query->addField('h', 'parent'); - $query->condition('t.vid', $vid); - $query->addTag('term_access'); - $query->orderBy('t.weight'); - $query->orderBy('t.name'); - $tid_parents = $query->execute()->fetchAllKeyed(); - $terms[$vid] = taxonomy_term_load_multiple(array_keys($tid_parents)); - - foreach ($tid_parents as $tid => $parent_tid) { - $children[$vid][$parent_tid][] = $tid; - $parents[$vid][$tid][] = $parent_tid; + $result = $query + ->addTag('translatable') + ->addTag('term_access') + ->fields('t') + ->fields('h', array('parent')) + ->condition('t.vid', $vid) + ->orderBy('t.weight') + ->orderBy('t.name') + ->execute(); + + foreach ($result as $term) { + $children[$vid][$term->parent][] = $term->tid; + $parents[$vid][$term->tid][] = $term->parent; + $terms[$vid][$term->tid] = $term; } } + // Load full entities, if necessary. The entity controller statically + // caches the results. + if ($load_entities) { + $term_entities = taxonomy_term_load_multiple(array_keys($terms[$vid])); + } + $max_depth = (!isset($max_depth)) ? count($children[$vid]) : $max_depth; $tree = array(); - if ($max_depth > $depth && !empty($children[$vid][$parent])) { - foreach ($children[$vid][$parent] as $child) { - $term = clone $terms[$vid][$child]; - $term->depth = $depth; - $term->parents = $parents[$vid][$child]; - $tree[] = $term; - if (!empty($children[$vid][$child])) { - $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $max_depth, $depth)); + + // Keeps track of the parents we have to process, the last entry is used + // for the next processing step. + $process_parents = array(); + $process_parents[] = $parent; + + // Loops over the parent terms and adds its children to the tree array. + // Uses a loop instead of a recursion, because it's more efficient. + while (count($process_parents)) { + $parent = array_pop($process_parents); + // The number of parents determines the current depth. + $depth = count($process_parents); + if ($max_depth > $depth && !empty($children[$vid][$parent])) { + $has_children = FALSE; + $child = current($children[$vid][$parent]); + do { + if (empty($child)) { + 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. + $term = clone $term; + } + $term->depth = $depth; + unset($term->parent); + $term->parents = $parents[$vid][$term->tid]; + $tree[] = $term; + if (!empty($children[$vid][$term->tid])) { + $has_children = TRUE; + + // We have to continue with this parent later. + $process_parents[] = $parent; + // Use the current term as parent for the next iteration. + $process_parents[] = $term->tid; + + // Reset pointers for child lists because we step in there more often + // with multi parents. + reset($children[$vid][$term->tid]); + // Move pointer so that we get the correct term the next time. + next($children[$vid][$parent]); + break; + } + } while ($child = next($children[$vid][$parent])); + + if (!$has_children) { + // We processed all terms in this hierarchy-level, reset pointer + // so that this function works the next time it gets called. + reset($children[$vid][$parent]); } } } diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index f15ef27c919e0534dc689be0e0f5cb368af898be..9fd841323aeb2bb67ae54bb18acfaf728334aac3 100644 --- a/modules/taxonomy/taxonomy.pages.inc +++ b/modules/taxonomy/taxonomy.pages.inc @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.pages.inc,v 1.55 2010/10/06 13:38:40 dries Exp $ +// $Id: taxonomy.pages.inc,v 1.56 2010/10/13 01:25:11 webchick Exp $ /** * @file @@ -91,8 +91,9 @@ function taxonomy_autocomplete($field_name, $tags_typed = '') { // Part of the criteria for the query come from the field's own settings. $vids = array(); + $vocabularies = taxonomy_vocabulary_get_names(); foreach ($field['settings']['allowed_values'] as $tree) { - $vids[] = $tree['vid']; + $vids[] = $vocabularies[$tree['vocabulary']]->vid; } $query = db_select('taxonomy_term_data', 't'); diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test index f530c6f0f0228037ad3509bfaa386e2352c701a4..c622d6a12c72e68325326c84a153530c808b2dd6 100644 --- a/modules/taxonomy/taxonomy.test +++ b/modules/taxonomy/taxonomy.test @@ -1,5 +1,5 @@ <?php -// $Id: taxonomy.test,v 1.93 2010/10/01 01:37:13 webchick Exp $ +// $Id: taxonomy.test,v 1.96 2010/10/13 13:43:21 dries Exp $ /** * @file @@ -92,7 +92,7 @@ class TaxonomyVocabularyFunctionalTest extends TaxonomyWebTestCase { // Try to submit a vocabulary with a duplicate machine name. $edit['machine_name'] = $machine_name; $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save')); - $this->assertText(t('This machine-readable name is already in use by another vocabulary and must be unique.'), t('Duplicate machine name validation was successful')); + $this->assertText(t('The machine-readable name is already in use. It must be unique.')); // Try to submit an invalid machine name. $edit['machine_name'] = '!&^%'; @@ -192,7 +192,7 @@ class TaxonomyVocabularyUnitTest extends TaxonomyWebTestCase { } function setUp() { - parent::setUp('taxonomy'); + parent::setUp('taxonomy', 'field_test'); $admin_user = $this->drupalCreateUser(array('create article content', 'administer taxonomy')); $this->drupalLogin($admin_user); $this->vocabulary = $this->createVocabulary(); @@ -325,6 +325,32 @@ class TaxonomyVocabularyUnitTest extends TaxonomyWebTestCase { // Fetch vocabulary 1 by name and ID. $this->assertTrue(current(taxonomy_vocabulary_load_multiple(array($vocabulary1->vid), array('name' => $vocabulary1->name)))->vid == $vocabulary1->vid, t('Vocabulary loaded successfully by name and ID.')); } + + /** + * Tests that machine name changes are properly reflected. + */ + function testTaxonomyVocabularyChangeMachineName() { + // Add a field instance to the vocabulary. + $field = array( + 'field_name' => 'field_test', + 'type' => 'test_field', + ); + field_create_field($field); + $instance = array( + 'field_name' => 'field_test', + 'entity_type' => 'taxonomy_term', + 'bundle' => $this->vocabulary->machine_name, + ); + field_create_instance($instance); + + // Change the machine name. + $new_name = drupal_strtolower($this->randomName()); + $this->vocabulary->machine_name = $new_name; + taxonomy_vocabulary_save($this->vocabulary); + + // Check that the field instance is still attached to the vocabulary. + $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), t('The bundle name was updated correctly.')); + } } /** @@ -466,7 +492,7 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $this->drupalGet('node/' . $node->nid); $this->assertText($term2->name, t('Term is displayed when viewing the node.')); - //Preview the node + // Preview the node. $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Preview')); $this->assertNoUniqueText($term2->name, t('Term is displayed when previewing the node.')); $this->drupalPost(NULL, NULL, t('Preview')); @@ -530,6 +556,11 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $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->assertNoText($term1->name, t('The deleted term %name does not appear on the node page.', array('%name' => $term1->name))); + + // Test autocomplete on term 2. + $input = substr($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))); } /** @@ -586,7 +617,7 @@ class TaxonomyTermTestCase extends TaxonomyWebTestCase { $term->description = ''; taxonomy_term_save($term); $this->drupalGet('taxonomy/term/' . $term->tid); - $this->assertNoPattern('|class="term-listing-heading"|', 'Term page did not display the term description when description was blank.'); + $this->assertNoPattern('|class="term-listing-heading"|', 'Term page did not display the term description when description was blank.'); // Check that the term feed page is working. $this->drupalGet('taxonomy/term/' . $term->tid . '/feed'); @@ -830,15 +861,9 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer taxonomy')); $this->drupalLogin($web_user); $this->vocabulary = $this->createVocabulary(); - } - /** - * Test term field validation. - */ - function testTaxonomyTermFieldValidation() { + // Setup a field and instance. $this->field_name = drupal_strtolower($this->randomName()); - - // Create a field with settings to validate. $this->field = array( 'field_name' => $this->field_name, 'type' => 'taxonomy_term_reference', @@ -866,7 +891,12 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { ), ); field_create_instance($this->instance); + } + /** + * Test term field validation. + */ + function testTaxonomyTermFieldValidation() { // Test valid and invalid values with field_attach_validate(). $langcode = LANGUAGE_NONE; $entity = field_test_create_stub_entity(); @@ -896,38 +926,6 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { * Test widgets. */ function testTaxonomyTermFieldWidgets() { - // Setup a field and instance. - $entity_type = 'test_entity'; - $this->field_name = drupal_strtolower($this->randomName()); - $this->field = array( - 'field_name' => $this->field_name, - 'type' => 'taxonomy_term_reference', - 'settings' => array( - 'allowed_values' => array( - array( - 'vocabulary' => $this->vocabulary->machine_name, - 'parent' => '0', - ), - ), - ) - ); - field_create_field($this->field); - $this->instance = array( - 'field_name' => $this->field_name, - 'entity_type' => 'test_entity', - 'bundle' => 'test_bundle', - 'label' => $this->randomName() . '_label', - 'widget' => array( - 'type' => 'options_select', - ), - 'display' => array( - 'full' => array( - 'type' => 'taxonomy_term_reference_link', - ), - ), - ); - field_create_instance($this->instance); - // Create a term in the vocabulary. $term = $this->createTerm($this->vocabulary); @@ -948,11 +946,45 @@ class TaxonomyTermFieldTestCase extends TaxonomyWebTestCase { // Display the object. $entity = field_test_entity_test_load($id); $entities = array($id => $entity); - field_attach_prepare_view($entity_type, $entities, 'full'); - $entity->content = field_attach_view($entity_type, $entity, 'full'); + field_attach_prepare_view('test_entity', $entities, 'full'); + $entity->content = field_attach_view('test_entity', $entity, 'full'); $this->content = drupal_render($entity->content); $this->assertText($term->name, t('Term name is displayed')); } + + /** + * Tests that vocabulary machine name changes are mirrored in field definitions. + */ + function testTaxonomyTermFieldChangeMachineName() { + // Add several entries in the 'allowed_values' setting, to make sure that + // they all get updated. + $this->field['settings']['allowed_values'] = array( + array( + 'vocabulary' => $this->vocabulary->machine_name, + 'parent' => '0', + ), + array( + 'vocabulary' => $this->vocabulary->machine_name, + 'parent' => '0', + ), + array( + 'vocabulary' => 'foo', + 'parent' => '0', + ), + ); + field_update_field($this->field); + // Change the machine name. + $new_name = drupal_strtolower($this->randomName()); + $this->vocabulary->machine_name = $new_name; + taxonomy_vocabulary_save($this->vocabulary); + + // Check that the field instance is still attached to the vocabulary. + $field = field_info_field($this->field_name); + $allowed_values = $field['settings']['allowed_values']; + $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, t('Index 0: Machine name was updated correctly.')); + $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, t('Index 1: Machine name was updated correctly.')); + $this->assertEqual($allowed_values[2]['vocabulary'], 'foo', t('Index 2: Machine name was left untouched.')); + } } /** @@ -1036,7 +1068,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $tests['[term:node-count]'] = 0; $tests['[term:parent:name]'] = '[term:parent:name]'; $tests['[term:vocabulary:name]'] = check_plain($this->vocabulary->name); - + foreach ($tests as $input => $expected) { $output = token_replace($input, array('term' => $term1), array('language' => $language)); $this->assertFalse(strcmp($output, $expected), t('Sanitized taxonomy term token %token replaced.', array('%token' => $input))); diff --git a/modules/toolbar/toolbar.info b/modules/toolbar/toolbar.info index c03f9aa336e96f3d7bf4323db4ae6d31d5b54094..680f0e2d346a7225751458897cf142420dcb49fb 100644 --- a/modules/toolbar/toolbar.info +++ b/modules/toolbar/toolbar.info @@ -6,8 +6,8 @@ package = Core version = VERSION files[] = toolbar.module -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/tracker/tracker.info b/modules/tracker/tracker.info index c94334f0cf073ec05706a9c700a4175692b5cd87..890725a009666ef8cec2106f13981cb48d97e4a4 100644 --- a/modules/tracker/tracker.info +++ b/modules/tracker/tracker.info @@ -9,8 +9,8 @@ files[] = tracker.module files[] = tracker.pages.inc files[] = tracker.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/translation/tests/translation_test.info b/modules/translation/tests/translation_test.info new file mode 100644 index 0000000000000000000000000000000000000000..78435978facd05318fd8f6a7fa422b0d694e2872 --- /dev/null +++ b/modules/translation/tests/translation_test.info @@ -0,0 +1,14 @@ +; $Id: translation_test.info,v 1.1 2010/10/09 03:22:30 webchick Exp $ +name = "Content Translation Test" +description = "Support module for the content translation tests." +core = 7.x +package = Testing +files[] = translation_test.module +version = VERSION +hidden = TRUE + +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" +project = "drupal" +datestamp = "1287812130" + diff --git a/modules/translation/tests/translation_test.module b/modules/translation/tests/translation_test.module new file mode 100644 index 0000000000000000000000000000000000000000..ef02ee00220f6b28f9db1c8b30b534f787b84afc --- /dev/null +++ b/modules/translation/tests/translation_test.module @@ -0,0 +1,14 @@ +<?php +// $Id: translation_test.module,v 1.1 2010/10/09 03:22:30 webchick Exp $ + +/** + * @file + * Mock module for content translation tests. + */ + +/** + * Implements hook_node_insert(). + */ +function translation_test_node_insert($node) { + drupal_write_record('node', $node, 'nid'); +} diff --git a/modules/translation/translation.info b/modules/translation/translation.info index 0a76075c1dea9ae92333c2d8cf99f8299e3b5663..7ebbc1e4c12e43e96c5fea6e81895f88c9232c79 100644 --- a/modules/translation/translation.info +++ b/modules/translation/translation.info @@ -9,8 +9,8 @@ files[] = translation.module files[] = translation.pages.inc files[] = translation.test -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/translation/translation.module b/modules/translation/translation.module index 486422ed1400d908bf01d3c72f5256178df3c39c..e236233c77e819ee9bba3310a0ee8db2c3d92668 100644 --- a/modules/translation/translation.module +++ b/modules/translation/translation.module @@ -1,5 +1,5 @@ <?php -// $Id: translation.module,v 1.84 2010/09/09 23:01:48 dries Exp $ +// $Id: translation.module,v 1.88 2010/10/09 17:38:41 webchick Exp $ /** * @file @@ -126,6 +126,24 @@ function translation_form_node_type_form_alter(&$form, &$form_state) { function translation_form_node_form_alter(&$form, &$form_state) { if (translation_supported_type($form['#node']->type)) { $node = $form['#node']; + $languages = language_list('enabled'); + $disabled_languages = isset($languages[0]) ? $languages[0] : FALSE; + $translator_widget = $disabled_languages && user_access('translate content'); + $groups = array(t('Disabled'), t('Enabled')); + // Allow translators to enter content in disabled languages. Translators + // might need to distinguish between enabled and disabled languages, hence + // we divide them in two option groups. + if ($translator_widget) { + $options = array(); + $language_list = locale_language_list('name', TRUE); + foreach (array(1, 0) as $status) { + $group = $groups[$status]; + foreach ($languages[$status] as $langcode => $language) { + $options[$group][$langcode] = $language_list[$langcode]; + } + } + $form['language']['#options'] = $options; + } if (!empty($node->translation_source)) { // We are creating a translation. Add values and lock language field. $form['translation_source'] = array('#type' => 'value', '#value' => $node->translation_source); @@ -136,9 +154,15 @@ function translation_form_node_form_alter(&$form, &$form_state) { // node to some language which is already in the translation set. Also remove the // language neutral option. unset($form['language']['#options'][LANGUAGE_NONE]); - foreach (translation_node_get_translations($node->tnid) as $translation) { + foreach (translation_node_get_translations($node->tnid) as $langcode => $translation) { if ($translation->nid != $node->nid) { - unset($form['language']['#options'][$translation->language]); + if ($translator_widget) { + $group = $groups[(int)!isset($disabled_languages[$langcode])]; + unset($form['language']['#options'][$group][$langcode]); + } + else { + unset($form['language']['#options'][$langcode]); + } } } // Add translation values and workflow options. @@ -177,19 +201,57 @@ function translation_form_node_form_alter(&$form, &$form_state) { /** * Implements hook_node_view(). * - * Display translation links with native language names, if this node - * is part of a translation set. + * Display translation links with native language names, if this node is part of + * a translation set. If no language provider is enabled "fall back" to the + * simple links built through the result of translation_node_get_translations(). */ function translation_node_view($node, $view_mode) { + // If the site has no translations or is not multilingual we have no content + // translation links to display. if (isset($node->tnid) && drupal_multilingual() && $translations = translation_node_get_translations($node->tnid)) { - $path = 'node/' . $node->nid; - $links = language_negotiation_get_switch_links(LANGUAGE_TYPE_INTERFACE, $path); - if (is_object($links)) { - $links = $links->links; - // Do not show link to the same node. - unset($links[$node->language]); - $node->content['links']['#links'] = array_merge($node->content['links']['#links'], $links); + $languages = language_list('enabled'); + $languages = $languages[1]; + + // There might be a language provider enabled defining custom language + // switch links which need to be taken into account while generating the + // content translation links. As custom language switch links are available + // only for configurable language types and interface language is the only + // configurable language type in core, we use it as default. Contributed + // modules can change this behavior by setting the system variable below. + $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE); + $custom_links = language_negotiation_get_switch_links($type, "node/$node->nid"); + $links = array(); + + foreach ($translations as $langcode => $translation) { + // Do not show links to the same node, to unpublished translations or to + // translations in disabled languages. + if ($translation->status && isset($languages[$langcode]) && $langcode != $node->language) { + $language = $languages[$langcode]; + $key = "translation_$langcode"; + + if (isset($custom_links->links[$langcode])) { + $links[$key] = $custom_links->links[$langcode]; + } + else { + $links[$key] = array( + 'href' => "node/{$translation->nid}", + 'title' => $language->native, + 'language' => $language, + ); + } + + // Custom switch links are more generic than content translation links, + // hence we override existing attributes with the ones below. + $links[$key] += array('attributes' => array()); + $attributes = array( + 'title' => $translation->title, + 'class' => array('translation-link'), + ); + $links[$key]['attributes'] = $attributes + $links[$key]['attributes']; + } } + + $node->content['links']['#links'] += $links; } } @@ -272,6 +334,8 @@ function translation_node_insert($node) { )) ->condition('nid', $node->nid) ->execute(); + // Save tnid to avoid loss in case of resave. + $node->tnid = $tnid; } } } @@ -384,7 +448,7 @@ function translation_node_get_translations($tnid) { if (!isset($translations[$tnid])) { $translations[$tnid] = array(); $result = db_select('node', 'n') - ->fields('n', array('nid', 'title', 'language')) + ->fields('n', array('nid', 'type', 'uid', 'status', 'title', 'language')) ->condition('n.tnid', $tnid) ->addTag('node_access') ->execute(); @@ -434,9 +498,13 @@ function translation_path_get_translations($path) { * Replaces links with pointers to translated versions of the content. */ function translation_language_switch_links_alter(array &$links, $type, $path) { - if ($type == LANGUAGE_TYPE_INTERFACE && $paths = translation_path_get_translations($path)) { + $language_type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE); + if ($type == $language_type && $paths = translation_path_get_translations($path)) { + $path = explode('/', $path); + $node = node_load($path[1]); + $translations = translation_node_get_translations($node->tnid); foreach ($links as $langcode => $link) { - if (isset($paths[$langcode])) { + if (isset($paths[$langcode]) && $translations[$langcode]->status) { // Translation in a different node. $links[$langcode]['href'] = $paths[$langcode]; } diff --git a/modules/translation/translation.test b/modules/translation/translation.test index 2b4f6d2268b2e6649d7e30eb23ec72bffa1efa5a..646097cf73c2365aeaf3e12b645d6563cd3f7e55 100644 --- a/modules/translation/translation.test +++ b/modules/translation/translation.test @@ -1,5 +1,5 @@ <?php -// $Id: translation.test,v 1.29 2010/08/05 23:53:39 webchick Exp $ +// $Id: translation.test,v 1.32 2010/10/09 17:38:41 webchick Exp $ class TranslationTestCase extends DrupalWebTestCase { protected $book; @@ -13,18 +13,13 @@ class TranslationTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp('locale', 'translation'); - } + parent::setUp('locale', 'translation', 'translation_test'); - /** - * Create a basic page with translation, modify the basic page outdating translation, and update translation. - */ - function testContentTranslation() { // Setup users. - $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages')); - $translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate content')); + $this->admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages')); + $this->translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate content')); - $this->drupalLogin($admin_user); + $this->drupalLogin($this->admin_user); // Add languages. $this->addLanguage('en'); @@ -37,9 +32,14 @@ class TranslationTestCase extends DrupalWebTestCase { $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type')); $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.')); - $this->drupalLogout(); - $this->drupalLogin($translator); + $this->drupalLogin($this->translator); + } + /** + * Create a basic page with translation, modify the basic page outdating + * translation, and update translation. + */ + function testContentTranslation() { // Create Basic page in English. $node_title = $this->randomName(); $node_body = $this->randomName(); @@ -83,6 +83,49 @@ class TranslationTestCase extends DrupalWebTestCase { $edit['translation[status]'] = FALSE; $this->drupalPost('node/' . $node_translation->nid . '/edit', $edit, t('Save')); $this->assertRaw(t('Basic page %title has been updated.', array('%title' => $node_translation_title)), t('Translated node updated.')); + + $this->drupalLogin($this->admin_user); + + // Disable Spanish and confirm that links to the Spanish translations do + // not appear on the English node. + $edit = array(); + $edit['enabled[es]'] = FALSE; + $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); + $this->drupalGet('node/' . $node->nid); + $languages = language_list(); + $this->assertNoText($languages['es']->native); + + $this->drupalLogin($this->translator); + + // Confirm that Spanish is still an option for translators when creating nodes. + $this->drupalGet('node/add/page'); + $this->assertRaw('value="' . 'es' .'"', t('Spanish is available in language selection')); + } + + /** + * Check that content translation links behave properly. + */ + function testContentTranslationLinks() { + // Create Basic page in English. + $node_title = $this->randomName(); + $node_body = $this->randomName(); + $node = $this->createPage($node_title, $node_body, 'en'); + + // Submit translation in Spanish. + $node_translation_title = $this->randomName(); + $node_translation_body = $this->randomName(); + $node_translation = $this->createTranslation($node, $node_translation_title, $node_translation_body, 'es'); + + // Check that content translation links are shown even when no language + // negotiation is configured. + $languages = language_list(); + $this->drupalGet("node/$node->nid"); + $url = url("node/$node_translation->nid"); + $this->assertContentByXPath('//a[@href=:url]', array(':url' => $url), $languages['es']->native, t('Spanish translation link found.')); + + $this->drupalGet("node/$node_translation->nid"); + $url = url("node/$node->nid"); + $this->assertContentByXPath('//a[@href=:url]', array(':url' => $url), $languages['en']->native, t('English translation link found.')); } /** @@ -145,10 +188,46 @@ class TranslationTestCase extends DrupalWebTestCase { return $node; } + /** + * Assert that an element identified by the given XPath has the given content. + * + * @param $xpath + * XPath used to find the element. + * @param array $arguments + * An array of arguments with keys in the form ':name' matching the + * placeholders in the query. The values may be either strings or numeric + * values. + * @param $value + * The text content of the matched element to assert. + * @param $message + * Message to display. + * @param $group + * The group this message belongs to. + * + * @return + * TRUE on pass, FALSE on fail. + */ + function assertContentByXPath($xpath, array $arguments = array(), $value = NULL, $message = '', $group = 'Other') { + $elements = $this->xpath($xpath, $arguments); + + $found = TRUE; + if ($value && $elements) { + $found = FALSE; + foreach ($elements as $element) { + if ((string) $element == $value) { + $found = TRUE; + break; + } + } + } + + return $this->assertTrue($elements && $found, $message, $group); + } + /** * Create a translation for the specified basic page in the specified language. * - * @param integer $nid Node id of basic page to create translation for. + * @param object $node The basic page to create translation for. * @param string $title Title of basic page in specified language. * @param string $body Body of basic page in specified language. * @param string $language Language code. @@ -167,9 +246,10 @@ class TranslationTestCase extends DrupalWebTestCase { $this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Translation created.')); // Check to make sure that translation was successful. - $node = $this->drupalGetNodeByTitle($title); - $this->assertTrue($node, t('Node found in database.')); + $translation = $this->drupalGetNodeByTitle($title); + $this->assertTrue($translation, t('Node found in database.')); + $this->assertTrue($translation->tnid == $node->nid, t('Translation set id correctly stored.')); - return $node; + return $translation; } } diff --git a/modules/trigger/tests/trigger_test.info b/modules/trigger/tests/trigger_test.info index a1e228163d169e93504273d263f49e082f132888..1fdbb6ee948c4cdebb4bd075c1fb7b81805cf4e9 100644 --- a/modules/trigger/tests/trigger_test.info +++ b/modules/trigger/tests/trigger_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = trigger_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/trigger/trigger.admin.inc b/modules/trigger/trigger.admin.inc index 88143201cc11bed4ec5ba4ae2779162d465b7c24..0bd205aa327f03c4609c33b493e34cae20515db6 100644 --- a/modules/trigger/trigger.admin.inc +++ b/modules/trigger/trigger.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: trigger.admin.inc,v 1.28 2010/09/24 21:36:22 dries Exp $ +// $Id: trigger.admin.inc,v 1.30 2010/10/20 16:19:24 webchick Exp $ /** * @file @@ -179,7 +179,10 @@ function trigger_assign_form($form, $form_state, $module, $hook, $label) { if (count($options) != 0) { $form[$hook]['parent']['aid'] = array( '#type' => 'select', + '#title' => t('List of trigger actions when !description', array('!description' => $label)), + '#title_display' => 'invisible', '#options' => $options, + '#empty_option' => t('Choose an action'), ); $form[$hook]['parent']['submit'] = array( '#type' => 'submit', diff --git a/modules/trigger/trigger.info b/modules/trigger/trigger.info index 72ef21e5cda585aef816a37153a806bae617f635..b81c48517d29f7b64190071fddfc2c6358d19a1e 100644 --- a/modules/trigger/trigger.info +++ b/modules/trigger/trigger.info @@ -10,8 +10,8 @@ files[] = trigger.install files[] = trigger.test configure = admin/structure/trigger -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/update/tests/aaa_update_test.info b/modules/update/tests/aaa_update_test.info index 7f2d54483cc42e624d501b18c4eb288198d423c9..9b31a7af0bd7b153e24b432eeac53b7d4e242158 100644 --- a/modules/update/tests/aaa_update_test.info +++ b/modules/update/tests/aaa_update_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = aaa_update_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/update/tests/bbb_update_test.info b/modules/update/tests/bbb_update_test.info index 6c2fdabefa8b65bb0509c85b2357ca45393ce72e..12e5a654660480dafe73da90356d4b1cc9aa4b0c 100644 --- a/modules/update/tests/bbb_update_test.info +++ b/modules/update/tests/bbb_update_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = bbb_update_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/update/tests/ccc_update_test.info b/modules/update/tests/ccc_update_test.info index b6d978b1a7347a7c0048461881d0f31d4f46f11b..76f326eff61cc68f3c61c32583249631c4b07f07 100644 --- a/modules/update/tests/ccc_update_test.info +++ b/modules/update/tests/ccc_update_test.info @@ -6,8 +6,8 @@ core = 7.x files[] = ccc_update_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/update/tests/update_test.info b/modules/update/tests/update_test.info index ab7b0594aefd2c4de3a51621d04df9484666621b..6fe5c5707f9c1aadae1aa04aa84e25ee4134c11e 100644 --- a/modules/update/tests/update_test.info +++ b/modules/update/tests/update_test.info @@ -7,8 +7,8 @@ core = 7.x files[] = update_test.module hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/update/update.css b/modules/update/update.css index f19db9b640a657aa4cdde515e5ba22c516d38be5..832195e329f4b45300c70f96f057d0e7dbb2ad2a 100644 --- a/modules/update/update.css +++ b/modules/update/update.css @@ -1,4 +1,4 @@ -/* $Id: update.css,v 1.7 2010/04/28 20:08:39 dries Exp $ */ +/* $Id: update.css,v 1.8 2010/10/08 00:24:09 dries Exp $ */ .update .project { font-weight: bold; @@ -27,6 +27,11 @@ padding: 1em 1em .25em 1em; } +.update tr.even, +.update tr.odd { + border: none; +} + .update tr td { border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; @@ -65,6 +70,7 @@ table.update, .update table.version { width: 100%; margin-top: .5em; + border: none; } .update table.version tbody { @@ -77,6 +83,7 @@ table.update, padding: 0; margin: 0; border: none; + background: none; } .update table.version .version-title { diff --git a/modules/update/update.info b/modules/update/update.info index d9a81efbde12562fa273dea711f7bb8ec30ce16b..24eb18ab367c611fae4dca1adf268c2129b6c9ae 100644 --- a/modules/update/update.info +++ b/modules/update/update.info @@ -15,8 +15,8 @@ files[] = update.settings.inc files[] = update.test configure = admin/reports/updates/settings -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc index 35a5717beb8ecbac2fd2c5bcc554a789835b345f..9ced79d524e8f81f019fa2e7365af8ebe43e068c 100644 --- a/modules/user/user.admin.inc +++ b/modules/user/user.admin.inc @@ -1,5 +1,5 @@ <?php -// $Id: user.admin.inc,v 1.119 2010/10/06 13:38:40 dries Exp $ +// $Id: user.admin.inc,v 1.120 2010/10/20 01:31:07 dries Exp $ /** * @file @@ -183,6 +183,8 @@ function user_admin_account() { } $form['options']['operation'] = array( '#type' => 'select', + '#title' => t('Operation'), + '#title_display' => 'invisible', '#options' => $options, '#default_value' => 'unblock', ); @@ -835,6 +837,8 @@ function user_admin_roles($form, $form_state) { $form['roles'][$rid]['#weight'] = $order; $form['roles'][$rid]['weight'] = array( '#type' => 'textfield', + '#title' => t('Weight for @title', array('@title' => $name['label'])), + '#title_display' => 'invisible', '#size' => 4, '#default_value' => $order, '#attributes' => array('class' => array('role-weight')), @@ -844,6 +848,8 @@ function user_admin_roles($form, $form_state) { $form['name'] = array( '#type' => 'textfield', + '#title' => t('Name'), + '#title_display' => 'invisible', '#size' => 32, '#maxlength' => 64, ); diff --git a/modules/user/user.info b/modules/user/user.info index 8115e10e2cf2b0599e06b5270c234c933297de76..daea44507fb9ba78d663bd9759c1219439c90a5a 100644 --- a/modules/user/user.info +++ b/modules/user/user.info @@ -14,8 +14,8 @@ required = TRUE configure = admin/config/people stylesheets[all][] = user.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/modules/user/user.install b/modules/user/user.install index 217081162f09b26d9935e601f9ace73a3ba0980f..a6947277af103352d7221e040705ed96d475f073 100644 --- a/modules/user/user.install +++ b/modules/user/user.install @@ -1,5 +1,5 @@ <?php -// $Id: user.install,v 1.67 2010/10/05 06:17:29 webchick Exp $ +// $Id: user.install,v 1.68 2010/10/20 01:15:58 dries Exp $ /** * @file @@ -167,8 +167,8 @@ function user_schema() { 'description' => "User's signature.", ), 'signature_format' => array( - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 255, 'not null' => FALSE, 'description' => 'The {filter_format}.format of the signature.', ), @@ -355,6 +355,11 @@ function user_update_dependencies() { $dependencies['user'][7013] = array( 'system' => 7059, ); + // Ensure that format columns are only changed after Filter module has changed + // the primary records. + $dependencies['user'][7015] = array( + 'filter' => 7010, + ); return $dependencies; } @@ -837,6 +842,18 @@ function user_update_7014() { return t("Renamed the 'post comments without approval' permission to 'skip comment approval'."); } +/** + * Change {users}.signature_format into varchar. + */ +function user_update_7015() { + db_change_field('users', 'signature_format', 'signature_format', array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'The {filter_format}.format of the signature.', + )); +} + /** * @} End of "defgroup user-updates-6.x-to-7.x" * The next series of updates should start at 8000. diff --git a/modules/user/user.module b/modules/user/user.module index c78f3edd5c57aa7e36835091ba391be4c06bb8c7..cb3fc55dba227e922fee7cc387840f2305515451 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -1,5 +1,5 @@ <?php -// $Id: user.module,v 1.1206 2010/10/04 14:54:10 webchick Exp $ +// $Id: user.module,v 1.1212 2010/10/23 03:20:40 webchick Exp $ /** * @file @@ -1409,7 +1409,9 @@ function template_preprocess_user_picture(&$variables) { } if (isset($filepath)) { $alt = t("@user's picture", array('@user' => format_username($account))); - if (module_exists('image') && $style = variable_get('user_picture_style', '')) { + // If the image does not have a valid Drupal scheme (for eg. HTTP), + // don't load image styles. + if (module_exists('image') && file_valid_uri($filepath) && $style = variable_get('user_picture_style', '')) { $variables['user_picture'] = theme('image_style', array('style_name' => $style, 'path' => $filepath, 'alt' => $alt, 'title' => $alt)); } else { @@ -1520,15 +1522,12 @@ function user_menu() { // Registration and login pages. $items['user'] = array( 'title' => 'User account', + 'title callback' => 'user_menu_title', 'page callback' => 'user_page', 'access callback' => TRUE, - // Edge-case: No menu links should be auto-generated for this and below - // items, which makes it a MENU_CALLBACK. However, this item's title is - // expected to appear on user login, register, and password pages, so we - // need to use MENU_VISIBLE_IN_BREADCRUMB to make - // menu_get_active_breadcrumb() account for it. - 'type' => MENU_VISIBLE_IN_BREADCRUMB, 'file' => 'user.pages.inc', + 'weight' => -10, + 'menu_name' => 'user-menu', ); $items['user/login'] = array( @@ -1665,9 +1664,7 @@ function user_menu() { 'weight' => -10, ); - // Use %user_uid_only_optional here to avoid loading the full user for - // basic access checks. - $items['user/%user_uid_only_optional'] = array( + $items['user/%user'] = array( 'title' => 'My account', 'title callback' => 'user_page_title', 'title arguments' => array(1), @@ -1675,8 +1672,12 @@ function user_menu() { 'page arguments' => array(1), 'access callback' => 'user_view_access', 'access arguments' => array(1), - 'weight' => -10, - 'menu_name' => 'user-menu', + // By assigning a different menu name, this item (and all registered child + // paths) are no longer considered as children of 'user'. When accessing the + // user account pages, the preferred menu link that is used to build the + // active trail (breadcrumb) will be found in this menu (unless there is + // more specific link), so the link to 'user' will not be in the breadcrumb. + 'menu_name' => 'navigation', ); $items['user/%user/view'] = array( @@ -1784,6 +1785,35 @@ function user_menu_site_status_alter(&$menu_site_status, $path) { } } +/** + * Implements hook_menu_link_alter(). + */ +function user_menu_link_alter(&$link) { + // The path 'user' must be accessible for anonymous users, but only visible + // for authenticated users. Authenticated users should see "My account", but + // anonymous users should not see it at all. Therefore, invoke + // user_translated_menu_link_alter() to conditionally hide the link. + if ($link['link_path'] == 'user' && $link['module'] == 'system') { + $link['options']['alter'] = TRUE; + } + + // Force the Logout link to appear on the top-level of 'user-menu' menu by + // default (i.e., unless it has been customized). + if ($link['link_path'] == 'user/logout' && $link['module'] == 'system' && empty($link['customized'])) { + $link['plid'] = 0; + } +} + +/** + * Implements hook_translated_menu_link_alter(). + */ +function user_translated_menu_link_alter(&$link) { + // Hide the "User account" link for anonymous users. + if ($link['link_path'] == 'user' && $link['module'] == 'system' && user_is_anonymous()) { + $link['hidden'] = 1; + } +} + /** * Implements hook_admin_paths(). */ @@ -1796,6 +1826,17 @@ function user_admin_paths() { return $paths; } +/** + * Returns $arg or the user ID of the current user if $arg is '%' or empty. + * + * Deprecated. Use %user_uid_optional instead. + * + * @todo D8: Remove. + */ +function user_uid_only_optional_to_arg($arg) { + return user_uid_optional_to_arg($arg); +} + /** * Load either a specified or the current user account. * @@ -1867,24 +1908,19 @@ function user_uid_optional_to_arg($arg) { } /** - * Returns $arg or the user ID of the current user if $arg is '%' or empty. + * Menu item title callback for the 'user' path. * - * @todo rethink the naming of this in Drupal 8. + * Anonymous users should see "User account", but authenticated users are + * expected to see "My account". */ -function user_uid_only_optional_to_arg($arg) { - return user_uid_optional_to_arg($arg); +function user_menu_title() { + return user_is_logged_in() ? t('My account') : t('User account'); } /** * Menu item title callback - use the user name. */ -function user_page_title($uid) { - if ($GLOBALS['user']->uid == $uid) { - $account = $GLOBALS['user']; - } - else { - $account = user_load($uid); - } +function user_page_title($account) { return is_object($account) ? format_username($account) : ''; } @@ -2338,6 +2374,7 @@ function user_delete_multiple(array $uids) { foreach ($accounts as $uid => $account) { module_invoke_all('user_delete', $account); + module_invoke_all('entity_delete', $account, 'user'); field_attach_delete('user', $account); drupal_session_destroy_uid($account->uid); } @@ -2359,10 +2396,9 @@ function user_delete_multiple(array $uids) { /** * Page callback wrapper for user_view(). */ -function user_view_page($uid) { +function user_view_page($account) { // An administrator may try to view a non-existent account, // so we give them a 404 (versus a 403 for non-admins). - $account = user_load($uid); return is_object($account) ? user_view($account) : MENU_NOT_FOUND; } @@ -2825,19 +2861,23 @@ function user_permission_get_modules() { * * This function may be used to grant and revoke multiple permissions at once. * For example, when a form exposes checkboxes to configure permissions for a - * role, the submitted values may be directly passed on in a form submit - * handler. + * role, the form submit handler may directly pass the submitted values for the + * checkboxes form element to this function. * * @param $rid * The ID of a user role to alter. * @param $permissions - * An array of permissions, where the key holds the permission name and the - * value is an integer or boolean that determines whether to grant or revoke - * the permission: + * An associative array, where the key holds the permission name and the value + * determines whether to grant or revoke that permission. Any value that + * evaluates to TRUE will cause the permission to be granted. Any value that + * evaluates to FALSE will cause the permission to be revoked. * @code * array( - * 'administer nodes' => 0, - * 'access user profiles' => 1, + * 'administer nodes' => 0, // Revoke 'administer nodes' + * 'administer blocks' => FALSE, // Revoke 'administer blocks' + * 'access user profiles' => 1, // Grant 'access user profiles' + * 'access content' => TRUE, // Grant 'access content' + * 'access comments' => 'access comments', // Grant 'access comments' * ) * @endcode * Existing permissions are not changed, unless specified in $permissions. @@ -3680,3 +3720,27 @@ function user_file_download_access($field, $entity_type, $entity) { return user_view_access($entity); } } + +/** + * Implements hook_system_info_alter(). + * + * Drupal 7 ships with two methods to add additional fields to users: Profile + * module, a legacy module dating back from 2002, and Field API integration + * with users. While Field API support for users currently provides less end + * user features, the inefficient data storage mechanism of Profile module, as + * well as its lack of consistency with the rest of the entity / field based + * systems in Drupal 7, make this a sub-optimal solution to those who were not + * using it in previous releases of Drupal. + * + * To prevent new Drupal 7 sites from installing Profile module, and + * unwittingly ending up with two completely different and incompatible methods + * of extending users, remove it from the available modules by setting it to + * hidden if the profile_* tables are not already present. + * + * @todo: Remove in D8, pending upgrade path. + */ +function user_system_info_alter(&$info, $file, $type) { + if ($type == 'module' && $file->name == 'profile' && !db_table_exists('profile_field')) { + $info['hidden'] = TRUE; + } +} diff --git a/modules/user/user.test b/modules/user/user.test index c673b31321eacb832b77c6572ca0a310dbd04e1b..0fca3f8ae40324413ec7c810208c47bd12482bc5 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -1,5 +1,5 @@ <?php -// $Id: user.test,v 1.102 2010/10/05 06:17:29 webchick Exp $ +// $Id: user.test,v 1.104 2010/10/23 03:20:40 webchick Exp $ class UserRegistrationTestCase extends DrupalWebTestCase { public static function getInfo() { @@ -916,6 +916,26 @@ class UserPictureTestCase extends DrupalWebTestCase { } } + /** + * Test HTTP schema working with user pictures. + */ + function testExternalPicture() { + $this->drupalLogin($this->user); + // Set the default picture to an URI with a HTTP schema. + $images = $this->drupalGetTestFiles('image'); + $image = $images[0]; + $pic_path = file_create_url($image->uri); + variable_set('user_picture_default', $pic_path); + + // Check if image is displayed in user's profile page. + $this->drupalGet('user'); + + // Get the user picture image via xpath. + $elements = $this->xpath('//div[@class="user-picture"]/img'); + $this->assertEqual(count($elements), 1, t("There is exactly one user picture on the user's profile page")); + $this->assertEqual($pic_path, (string) $elements[0]['src'], t("User picture source is correct.")); + } + function saveUserPicture($image) { $edit = array('files[picture_upload]' => drupal_realpath($image->uri)); $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save')); @@ -1207,6 +1227,56 @@ class UserAutocompleteTestCase extends DrupalWebTestCase { } } + +/** + * Test user-links in secondary menu. + */ +class UserAccountLinksUnitTests extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'User account links', + 'description' => 'Test user-account links.', + 'group' => 'User' + ); + } + + /** + * Test the user login block. + */ + function testSecondaryMenu() { + // Create a regular user. + $user = $this->drupalCreateUser(array()); + + // Log in and get the homepage. + $this->drupalLogin($user); + $this->drupalGet('<front>'); + + // For a logged-in user, expect the secondary menu to have links for "My + // account" and "Log out". + $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( + ':menu_id' => 'secondary-menu-links', + ':href' => 'user', + ':text' => 'My account', + )); + $this->assertEqual(count($link), 1, 'My account link is in secondary menu.'); + + $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( + ':menu_id' => 'secondary-menu-links', + ':href' => 'user/logout', + ':text' => 'Log out', + )); + $this->assertEqual(count($link), 1, 'Log out link is in secondary menu.'); + + // Log out and get the homepage. + $this->drupalLogout(); + $this->drupalGet('<front>'); + + // For a logged-out user, expect no secondary links. + $element = $this->xpath('//ul[@id=:menu_id]', array(':menu_id' => 'secondary-menu-links')); + $this->assertEqual(count($element), 0, 'No secondary-menu for logged-out users.'); + } +} + /** * Test user blocks. */ diff --git a/modules/user/user.tokens.inc b/modules/user/user.tokens.inc index c0c7b3c6529482ab90c0ecd5456d65bf6a7c616b..e49b88c19cee64d2b5e5cdaf82fc18c33f7dea9a 100644 --- a/modules/user/user.tokens.inc +++ b/modules/user/user.tokens.inc @@ -1,5 +1,5 @@ <?php -// $Id: user.tokens.inc,v 1.7 2010/06/29 18:24:10 dries Exp $ +// $Id: user.tokens.inc,v 1.8 2010/10/16 20:09:17 webchick Exp $ /** * @file @@ -39,7 +39,7 @@ function user_token_info() { ); $user['edit-url'] = array( 'name' => t("Edit URL"), - 'description' => t("The url of the account edit page."), + 'description' => t("The URL of the account edit page."), ); $user['last-login'] = array( diff --git a/profiles/minimal/minimal.info b/profiles/minimal/minimal.info index 5d964888729247dc5b93ab790a7dc87a400243bf..3bc9fb568e6e1614ece2703656be714ad8147587 100644 --- a/profiles/minimal/minimal.info +++ b/profiles/minimal/minimal.info @@ -7,8 +7,8 @@ dependencies[] = block dependencies[] = dblog files[] = minimal.profile -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/profiles/standard/standard.info b/profiles/standard/standard.info index f5d6d1d05454cb295b0890e99cd634b1414804b5..904c4231a8a6e6233e0e69f8c8d358a13c928cee 100644 --- a/profiles/standard/standard.info +++ b/profiles/standard/standard.info @@ -23,8 +23,8 @@ dependencies[] = file dependencies[] = rdf files[] = standard.profile -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/profiles/standard/standard.install b/profiles/standard/standard.install index 681fbdd34ec655851160aa7e7b3938aa79bbbf58..10900ce1bb62658b6ea387c29edecab9826d9ecf 100644 --- a/profiles/standard/standard.install +++ b/profiles/standard/standard.install @@ -1,5 +1,5 @@ <?php -// $Id: standard.install,v 1.26 2010/10/05 06:17:29 webchick Exp $ +// $Id: standard.install,v 1.28 2010/10/21 04:22:34 webchick Exp $ /** * Implements hook_install(). @@ -9,6 +9,7 @@ function standard_install() { // Add text formats. $filtered_html_format = array( + 'format' => 'filtered_html', 'name' => 'Filtered HTML', 'weight' => 0, 'filters' => array( @@ -38,6 +39,7 @@ function standard_install() { filter_format_save($filtered_html_format); $full_html_format = array( + 'format' => 'full_html', 'name' => 'Full HTML', 'weight' => 1, 'filters' => array( @@ -115,16 +117,6 @@ function standard_install() { 'pages' => '', 'cache' => -1, ), - array( - 'module' => 'system', - 'delta' => 'management', - 'theme' => $default_theme, - 'status' => 1, - 'weight' => 1, - 'region' => 'sidebar_first', - 'pages' => '', - 'cache' => -1, - ), array( 'module' => 'system', 'delta' => 'powered-by', diff --git a/profiles/testing/testing.info b/profiles/testing/testing.info index c601cea8ab7e5f5f61c51d7976ecad0e81ef1c57..e6a31cbe3414a6ce7b0979bb400e6e7866010ea8 100644 --- a/profiles/testing/testing.info +++ b/profiles/testing/testing.info @@ -5,8 +5,8 @@ version = VERSION core = 7.x hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/profiles/unl_profile/unl_profile.install b/profiles/unl_profile/unl_profile.install index 372523ea2ad8dae41a990007a828ad77fc80b00b..f65a5cd3fd929b5c4462acdef532da5115fa4e34 100644 --- a/profiles/unl_profile/unl_profile.install +++ b/profiles/unl_profile/unl_profile.install @@ -226,13 +226,8 @@ EOF; 'default' => $new_prefix, // shared tables across all sites - 'authmap' => $shared_prefix, 'filter' => $shared_prefix, 'filter_format' => $shared_prefix, - 'role' => $shared_prefix, - 'sessions' => $shared_prefix, - 'users' => $shared_prefix, - 'users_roles' => $shared_prefix, 'wysiwyg' => $shared_prefix, ); @@ -243,6 +238,9 @@ EOF; foreach ($settings['databases']['value'] as &$database) { $database['default']['prefix'] = $new_prefixes; + if (!isset($database['slave'])) { + continue; + } foreach ($database['slave'] as &$slave_database) { $slave_database['prefix'] = $new_prefixes; } @@ -262,17 +260,45 @@ EOF; // Only enable CAS on subsites until we get some sort of bootstrap setup. module_enable(array('unl_cas')); - // Copy permissions from the parent site to the new site. - db_query("TRUNCATE ${new_prefix}role_permission")->execute(); - // I'm using REPLACE because I keep getting duplicate entry errors, despite inserting into what should be an empty table. - db_query("REPLACE INTO ${new_prefix}role_permission (rid, permission, module) SELECT rid, permission, module FROM ${shared_prefix}role_permission")->execute(); + + // Copy the anonymous/authenticated user roles' permissions for text formats from the default site. + db_query( + "REPLACE INTO {$new_prefix}role_permission (rid, permission, module) " + . "SELECT rid, permission, module " + . "FROM {$shared_prefix}role_permission " + . "WHERE rid IN(:rid) AND module='filter' ", + array(':rid' => array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID)) + ); + + db_query( + "REPLACE INTO {$new_prefix}variable (name, value) " + . "SELECT name, value " + . "FROM {$shared_prefix}variable " + . "WHERE name='filter_fallback_format' " + ); + + // Create the Site Admin role + $site_admin_role = new stdClass(); + $site_admin_role->name = 'Site Admin'; + $site_admin_role->weight = 2; + user_role_save($site_admin_role); + + // Assign most permissions to the Site Admin role. + $all_permissions = module_invoke_all('permission'); + unset($all_permissions['unl administer administrator permissions']); + unset($all_permissions['unl site creation']); + unset($all_permissions['administer modules']); + unset($all_permissions['administer themes']); + unset($all_permissions['administer software updates']); + unset($all_permissions['administer imce']); + unset($all_permissions['administer filters']); + foreach (array_keys(module_invoke('filter', 'permission')) as $permission) { + unset($all_permissions[$permission]); + } + user_role_grant_permissions($site_admin_role->rid, array_keys($all_permissions)); } - //$permissions = array_keys(module_invoke_all('permission')); - //print_r($permissions); - //user_role_grant_permissions(3, array($permissions)); - $files_dir = $settings_dir . '/files'; chmod($files_dir, 0777); diff --git a/sites/all/modules/unl/unl.module b/sites/all/modules/unl/unl.module index 0a7cf5b318e89aa5c62c371c14ad268ef94cc913..6eea4d43f23ec5c6854a094e5f708fadb020abc5 100644 --- a/sites/all/modules/unl/unl.module +++ b/sites/all/modules/unl/unl.module @@ -70,7 +70,20 @@ function unl_permission() { 'unl site creation' => array( 'title' => t('Site Creation'), 'description' => t('Create new drupal sites using the UNL profile'), - ) + 'restrict access' => TRUE, + ), + + 'unl grant all permissions' => array( + 'title' => t('Grant All Permissions'), + 'description' => t('If this is not checked, a user can only grant permissions that they themselves have. Requires the "Administer permissions" permission.'), + 'restrict access' => TRUE, + ), + + 'unl administer administrator permissions' => array( + 'title' => t('Administer Administrator\'s Permissions'), + 'description' => t('If this is not checked, a user can not change the permissions of the administrator role. Requires the "Administer permissions" permission.'), + 'restrict access' => TRUE, + ), ); } @@ -134,6 +147,73 @@ function unl_form_alter(&$form, $form_state, $form_id) { ); $form['#submit'][] = 'unl_system_settings_form_submit'; } + + if ($form_id == 'user_admin_permissions' && !in_array(variable_get('user_admin_role', -1), array_keys($GLOBALS['user']->roles))) { + + if (!user_access('unl grant all permissions')) { + // Remove permissions this user doesn't have from the headings list. + foreach ($form['permission'] as $permission => $sub_form) { + if (is_int($permission)) { + continue; + } + if (!user_access($permission)) { + unset($form['permission'][$permission]); + } + } + + // Remove any empty permission section headings. + $permission_sections = array_keys($form['permission']); + foreach ($permission_sections as $index => $permission_section) { + if (!is_int($permission_section)) { + continue; + } + if (!isset($permission_sections[$index + 1]) || is_int($permission_sections[$index + 1])) { + unset($form['permission'][$permission_section]); + } + } + + // Remove the permissions this user doesn't have from the checkboxes list. + foreach ($form['checkboxes'] as $role_id => $sub_form) { + foreach ($sub_form['#options'] as $permission => $value) { + if (!user_access($permission)) { + unset($form['checkboxes'][$role_id]['#options'][$permission]); + } + } + } + } + + if (!user_access('unl administer administrator permissions')) { + $role_id = variable_get('user_admin_role', -1); + unset($form['role_names'][$role_id]); + unset($form['role_names']['#value'][$role_id]); + unset($form['checkboxes'][$role_id]); + } + + if (!in_array(variable_get('user_admin_role'), array_keys($GLOBALS['user']->roles))) { + $administrator_permissions = array( + 'unl administer administrator permissions', + 'unl site creation', + 'administer modules', + 'administer themes', + 'administer software updates', + 'administer imce', + 'administer filters', + ); + + foreach ($form['permission'] as $permission => $sub_form) { + if (in_array($permission, $administrator_permissions)) { + unset($form['permission'][$permission]); + } + } + foreach ($form['checkboxes'] as $role_id => $sub_form) { + foreach ($sub_form['#options'] as $permission => $value) { + if (in_array($permission, $administrator_permissions)) { + unset($form['checkboxes'][$role_id]['#options'][$permission]); + } + } + } + } + } } function unl_system_settings_form_submit($form, &$form_state) { diff --git a/sites/all/modules/unl/unl_cas.admin.inc b/sites/all/modules/unl/unl_cas.admin.inc new file mode 100644 index 0000000000000000000000000000000000000000..1a211a6cf497529639c2d0faae4d727532195245 --- /dev/null +++ b/sites/all/modules/unl/unl_cas.admin.inc @@ -0,0 +1,68 @@ +<?php + +function unl_cas_user_import($form, &$form_state) { + $form['root']['#type'] = 'fieldset'; + $form['root']['#title'] = 'Import from peoplefinder.'; + $form['account']['name'] = array(); + $form['account']['name']['#type'] = 'textfield'; + $form['account']['name']['#title'] = 'Search String'; + $form['account']['name']['#maxlength'] = 60; + $form['account']['name']['#description'] = 'The name or username of the person to search for.'; +# $form['account']['name']['#required'] = 1; + $form['account']['name']['#attributes'] = array(); + $form['account']['name']['#attributes']['class'] = array(); + $form['account']['name']['#attributes']['class'][0] = 'username'; + $form['account']['name']['#default_value'] = ''; + $form['account']['name']['#access'] = 1; + $form['account']['name']['#weight'] = -10; + $form['submit']['#type'] = 'submit'; + $form['submit']['#value'] = 'Search'; + $form['submit']['#submit'] = array('unl_cas_user_import_search'); +# $form['submit']['#validate'] = array('unl_cas_user_validate'); + + if(isset($form_state['searchstring'])){ + $matches=array(); + $search=$form_state['searchstring']; + // TODO Use peoplefinder for now, use LDAP query once this app is granted access + $results = explode('</person>', file_get_contents('http://peoplefinder.unl.edu/service.php?q='.urlencode($search).'&format=xml&method=getLikeMatches')); + foreach($results as $result) { + $displayName=$uid=$affiliation=$mail=array(); + preg_match('/displayName>([^<]*)/', $result, $displayName); + preg_match('/<uid>([^<]*)/', $result, $uid); + preg_match('/<mail>([^<]*)/', $result, $mail); + preg_match('/<eduPersonPrimaryAffiliation>([^<]*)/', $result, $affiliation); + // TODO Need to handle incomplete results better + if(sizeof($uid)>1) { + $matches[$uid[1].'_'.(sizeof($mail)>1?$mail[1]:'')]=$displayName[1].' ('.$affiliation[1].') ('.$uid[1].')'; + } + } + $form['account']['radio']=array(); + $form['account']['radio']['#type'] = 'radios'; + $form['account']['radio']['#title'] = sizeof($matches).' Records Found'; +# $form['account']['radio']['#required'] = 1; + $form['account']['radio']['#options'] = $matches; + + $form['submit']['#value'] = 'Search Again'; + $form['submit2']['#type'] = 'submit'; + $form['submit2']['#value'] = 'Add Selected User'; + $form['submit2']['#submit'] = array('unl_cas_user_import_submit'); + } + return $form; +} + +function unl_cas_user_import_search($form, &$form_state) { + // if only one result is returned should we instead create the user? + $form_state['searchstring']=$form['account']['name']['#value']; + $form_state['rebuild']=TRUE; +} + +function unl_cas_user_import_submit($form, &$form_state) { + list($name, $mail) = explode('_', $form_state['complete form']['account']['radio']['#value']); + $userData = array( + 'name' => $name, 'mail' => $mail, 'status' => 1 + ); + $user = user_save(NULL, $userData); + if($user) drupal_set_message('<li>User '.$name.' successfully created.</li>'); +} + +?> diff --git a/sites/all/modules/unl/unl_cas.module b/sites/all/modules/unl/unl_cas.module index 339d9e6ecb15799225b4d38ef3ee0f218f37796b..78bd716fe71594213b570cf3a596e15eedb0b778 100644 --- a/sites/all/modules/unl/unl_cas.module +++ b/sites/all/modules/unl/unl_cas.module @@ -48,9 +48,24 @@ function unl_cas_menu() { 'access callback' => TRUE, ); + $items['admin/people/import'] = array( + 'title' => 'Import User', + 'description' => 'Import a user from UNL People Finder', + 'access arguments' => array('administer users'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('unl_cas_user_import'), + 'type' => MENU_LOCAL_ACTION, + 'file' => 'unl_cas.admin.inc', + ); + return $items; } +function unl_cas_menu_alter(&$items) +{ + unset($items['admin/people/create']); +} + function unl_cas_validate() { $cas = unl_cas_get_adapter(); @@ -146,7 +161,8 @@ function unl_cas_user_logout($account) { function unl_cas_import_user($username) { $xml = @file_get_contents('http://peoplefinder.unl.edu/service.php?format=xml&uid=' . $username); if ($xml) { - $dom = DOMDocument::loadXML($xml); + $dom = new DOMDocument(); + $dom->loadXML($xml); $firstName = $dom->getElementsByTagName('givenName')->item(0)->textContent; $lastName = $dom->getElementsByTagName('sn')->item(0)->textContent; $email = $dom->getElementsByTagName('mail')->item(0)->textContent; @@ -157,15 +173,85 @@ function unl_cas_import_user($username) { } $userData = array( - 'name' => $username, 'mail' => $email, 'status' => 1 + 'name' => $username, + 'mail' => $email, + 'status' => 1, + 'timezone' => variable_get('date_default_timezone', @date_default_timezone_get()), ); $user = user_save(NULL, $userData); return $user; } +/** + * Implements hook_user_presave() + * + * On non-default sites, only allow users who are administrators on the default + * to be administrators. Also, automatically make users who are administrators + * on the default site an administrator on non-default sites. + */ +function unl_cas_user_presave(&$edit, $account, $category) { + if (conf_path() == 'sites/default') { + return; + } + + if (isset($account->name)) { + $username = $account->name; + } + else { + $username = $edit['name']; + } + + $local_admin_role_id = variable_get('user_admin_role'); + + if (_unl_cas_is_user_default_site_administrator($username)) { + $local_admin_role = user_role_load($local_admin_role_id); + $edit['roles'][$local_admin_role_id] = $local_admin_role->name; + } + else { + unset($edit['roles'][$local_admin_role_id]); + } +} + +/** + * Implements hook_user_login() + * + * On non-default sites, if a user with the administrator role logs in, verify + * that they are still an admin in the default site. If not, remove them from + * the role. + */ +function unl_cas_user_login(&$edit, $account) { + if (conf_path() == 'sites/default') { + return; + } + + if (!in_array(variable_get('user_admin_role'), array_keys($account->roles))) { + return; + } + + $edit = array( + 'roles' => $account->roles, + ); + user_save($account, $edit); +} + +function _unl_cas_is_user_default_site_administrator($username) { + require 'sites/default/settings.php'; + $shared_prefix = $databases['default']['default']['prefix']; + + $data = db_query("SELECT value FROM {$shared_prefix}variable WHERE name='user_admin_role'")->fetchCol(); + $shared_admin_role_id = unserialize($data[0]); + + $shared_admin_usernames = db_query("SELECT u.name FROM {$shared_prefix}users AS u JOIN {$shared_prefix}users_roles AS r ON u.uid = r.uid WHERE name=:name AND rid=:rid", array(':name' => $username, ':rid' => $shared_admin_role_id))->fetchCol(); + return count($shared_admin_usernames) > 0; +} + function unl_cas_preprocess_user_picture(&$variables) { - //Default image: http://planetred.unl.edu/mod/profile/graphics/defaulttopbar.gif + //Default image: http://planetred.unl.edu/mod/profile/graphics/defaultmedium.gif + if ($variables['account']->uid == 0) { + $variables['user_picture'] = 'http://planetred.unl.edu/mod/profile/graphics/defaultmedium.gif'; + return; + } $username = $variables['account']->name; $variables['user_picture'] = '<img class="profile_pic medium" src="http://planetred.unl.edu/pg/icon/unl_' . $username . '/medium" alt="' . $username . '\'s photo" />'; } diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index d1bb23ad72cff19cd2e2558e053fcc9fefc7b624..6ebacffec1b74e00a3264daed77c9ac5a84b6305 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -1,5 +1,5 @@ <?php -// $Id: default.settings.php,v 1.50 2010/08/08 19:35:49 dries Exp $ +// $Id: default.settings.php,v 1.51 2010/10/11 23:49:48 dries Exp $ /** * @file @@ -377,6 +377,21 @@ ini_set('session.cookie_lifetime', 2000000); */ # $conf['omit_vary_cookie'] = TRUE; +/** + * CSS/JS aggregated file gzip compression: + * + * By default, when CSS or JS aggregation and clean URLs are enabled Drupal will + * store a gzip compressed (.gz) copy of the aggregated files. If this file is + * available then rewrite rules in the default .htaccess file will serve these + * files to browsers that accept gzip encoded content. This allows pages to load + * faster for these users and has minimal impact on server load. If you are + * using a webserver other than Apache httpd, or a caching reverse proxy that is + * configured to cache and compress these files itself you may want to uncomment + * one or both of the below lines, which will prevent gzip files being stored. + */ +# $conf['css_gzip_compression'] = FALSE; +# $conf['js_gzip_compression'] = FALSE; + /** * String overrides: * diff --git a/themes/bartik/bartik.info b/themes/bartik/bartik.info index 2010fec6ed6a5336671f7a55346e14f101642a59..0de257cfb63f059854fcebaa02500a95d3eb877f 100644 --- a/themes/bartik/bartik.info +++ b/themes/bartik/bartik.info @@ -36,8 +36,8 @@ regions[footer] = Footer settings[shortcut_module_link] = 0 -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/themes/bartik/css/ie-rtl.css b/themes/bartik/css/ie-rtl.css index d2cc4ccf38278cba561b9a617cdd8d764d080e8d..bf92cb6e010651a64f622837f8087c1184dec927 100644 --- a/themes/bartik/css/ie-rtl.css +++ b/themes/bartik/css/ie-rtl.css @@ -1,4 +1,4 @@ -/* $Id: ie-rtl.css,v 1.3 2010/10/05 01:48:11 dries Exp $ */ +/* $Id: ie-rtl.css,v 1.4 2010/10/07 17:59:01 webchick Exp $ */ fieldset legend { left: 6px; @@ -26,3 +26,13 @@ ul.action-links li a, padding-right: 20px; zoom: 1; } +#main-menu ul.links li { + margin: 0; +} +#main-menu ul.links li, +#main-menu ul.links li a { + display: inline; + float: none; + margin: 0; + zoom: 1; +} diff --git a/themes/bartik/css/ie.css b/themes/bartik/css/ie.css index ff0e767be1c7a0032758776b75cb965717fa1fb8..9ed85a84a1e135a99954028d03de70cb5ecaa3f9 100644 --- a/themes/bartik/css/ie.css +++ b/themes/bartik/css/ie.css @@ -1,4 +1,4 @@ -/* $Id: ie.css,v 1.6 2010/10/05 01:48:11 dries Exp $ */ +/* $Id: ie.css,v 1.7 2010/10/08 03:36:03 webchick Exp $ */ .block { zoom: 1; @@ -30,3 +30,9 @@ fieldset legend { #search-form input.form-submit:focus { background-position: center -25px; } +.contact-form #edit-message { + width: 75%; +} +.contact-form .resizable-textarea .grippie { + width: 76.3%; +} diff --git a/themes/bartik/css/style-rtl.css b/themes/bartik/css/style-rtl.css index a31c9f23fd269d4f826d0c59b24193890a8e8de4..2c26c2755202c8716af8f7b837e9a3e4ff35e2f6 100644 --- a/themes/bartik/css/style-rtl.css +++ b/themes/bartik/css/style-rtl.css @@ -1,4 +1,4 @@ -/* $Id: style-rtl.css,v 1.11 2010/10/05 01:48:11 dries Exp $ */ +/* $Id: style-rtl.css,v 1.12 2010/10/07 17:59:01 webchick Exp $ */ /* ------------------ Reset Styles ------------------ */ @@ -64,6 +64,13 @@ ul.tips { padding-right: 20px; } +/* --------------- Main Menu ------------ */ + +#main-menu ul.links li, +#main-menu ul.links li a { + float: right; +} + /* --------------- Secondary Menu ------------ */ #secondary-menu-links { diff --git a/themes/bartik/css/style.css b/themes/bartik/css/style.css index e46383e187c9ce23139433c3b74533d2a6032924..6000dcd80dec9af2861f7678e1a8c302c31b617f 100644 --- a/themes/bartik/css/style.css +++ b/themes/bartik/css/style.css @@ -1,4 +1,4 @@ -/* $Id: style.css,v 1.22 2010/10/05 01:48:11 dries Exp $ */ +/* $Id: style.css,v 1.26 2010/10/23 00:58:51 webchick Exp $ */ /* ---------- Overall Specifications ---------- */ @@ -419,7 +419,7 @@ h1#site-name, width: 154px; } /* Language switcher block in region header. */ -#block-locale-language ul li { +.region-header .block-locale ul li { display: inline; padding: 0 0.5em; } @@ -427,7 +427,6 @@ h1#site-name, /* --------------- Main Menu ------------ */ #main-menu { - padding: 0 15px 3px; clear: both; } #main-menu-links a { @@ -436,26 +435,35 @@ h1#site-name, } #main-menu-links { font-size: 0.929em; - padding: 2px 0; - padding: 0; + padding: 0 15px; +} +#main-menu-links li { + float: left; /* LTR */ + padding: 0 1px; + margin: 0 1px; } #main-menu-links a { color: #333; background: #ccc; background: rgba(255, 255, 255, 0.7); + float: left; /* LTR */ + height: 2.4em; + line-height: 2.4em; + padding: 0 0.8em; + text-decoration: none; text-shadow: 0 1px #eee; -khtml-border-radius-topleft: 8px; - -moz-border-radius-topleft: 8px; - -webkit-border-top-left-radius: 8px; - border-top-left-radius: 8px; -khtml-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; -moz-border-radius-topright: 8px; + -webkit-border-top-left-radius: 8px; -webkit-border-top-right-radius: 8px; + border-top-left-radius: 8px; border-top-right-radius: 8px; } #main-menu-links a:hover, #main-menu-links a:focus { - background: #fff; + background: #f6f6f2; background: rgba(255, 255, 255, 0.95); } #main-menu-links a:active { @@ -465,16 +473,11 @@ h1#site-name, #main-menu-links li a.active { border-bottom: none; } -.featured #main-menu-links li a.active, -.featured #main-menu-links li.active-trail a { +.featured #main-menu-links li a:active, +.featured #main-menu-links li a.active { background: #f0f0f0; background: rgba(240, 240, 240, 1.0); } -#main-menu-links li { - display: inline; - list-style-type: none; - padding: 0.6em 0 0.4em; -} /* --------------- Secondary Menu ------------ */ @@ -1156,7 +1159,50 @@ fieldset .description { .form-actions { padding-top: 10px; } - +/* Contact Form */ +.contact-form #edit-name { + width: 75%; + -khtml-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.contact-form #edit-mail { + width: 75%; + -khtml-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.contact-form #edit-subject { + width: 75%; + -khtml-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; +} +.contact-form #edit-message { + width: 76.3%; + -khtml-border-top-left-radius: 4px; + -khtml-border-top-right-radius: 4px; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.contact-form .resizable-textarea .grippie { + width: 76%; + -khtml-border-bottom-left-radius: 4px; + -khtml-border-bottom-right-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} /* Animated throbber */ html.js input.form-autocomplete { background-position: 100% 4px; /* LTR */ @@ -1290,6 +1336,9 @@ div.add-or-remove-shortcuts { .page-admin #content img { margin-right: 15px; /* LTR */ } +.page-admin #content .simpletest-image img { + margin: 0; +} .page-admin-structure-block-demo .block-region { color: #000000; } diff --git a/themes/garland/garland.info b/themes/garland/garland.info index 14394c31e55ad657a455e4d872e7621c9ae8549b..b651e4cdb913b72f651b93d7d3e6c980bbba0ecf 100644 --- a/themes/garland/garland.info +++ b/themes/garland/garland.info @@ -9,8 +9,8 @@ stylesheets[all][] = style.css stylesheets[print][] = print.css settings[garland_width] = fluid -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/themes/seven/seven.info b/themes/seven/seven.info index e30ee0652c125dac9c95ed6e85902d90cb74087a..e67bf55dd60b697c940f7e5e94c0ec904f7b71bd 100644 --- a/themes/seven/seven.info +++ b/themes/seven/seven.info @@ -15,8 +15,8 @@ regions[page_bottom] = Page bottom regions[sidebar_first] = First sidebar regions_hidden[] = sidebar_first -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/themes/seven/style-rtl.css b/themes/seven/style-rtl.css index f655bf717bc7b0f49026a912f5df4acc293fb8df..755ad2fb6b05c43ae7635134ff53319ef269d615 100644 --- a/themes/seven/style-rtl.css +++ b/themes/seven/style-rtl.css @@ -1,4 +1,4 @@ -/* $Id: style-rtl.css,v 1.1 2010/09/19 18:35:42 dries Exp $ */ +/* $Id: style-rtl.css,v 1.2 2010/10/15 04:37:15 webchick Exp $ */ /** * Generic elements. @@ -18,3 +18,8 @@ ul, margin-left: 0; margin-right: 1.5em; } + +/* Sortable tables */ +table th.active a { + padding: 0 0 0 25px; +} diff --git a/themes/seven/style.css b/themes/seven/style.css index 89c8d4c5bb390bca3d5e82f5beb059d96c0b251a..5e293bfb3d748b6c81dc33a518798ea5d530aaaa 100644 --- a/themes/seven/style.css +++ b/themes/seven/style.css @@ -1,4 +1,4 @@ -/* $Id: style.css,v 1.76 2010/10/05 21:03:17 webchick Exp $ */ +/* $Id: style.css,v 1.80 2010/10/15 04:37:15 webchick Exp $ */ /** * Generic elements. @@ -31,7 +31,7 @@ h3, h4, h5, h6 { - font-weight: normal; + font-weight: bold; margin: 10px 0; } h1 { @@ -476,6 +476,9 @@ table th a { display: block; position: relative; } +table th.active a { + padding: 0 25px 0 0; /* LTR */ +} table th.active img { position: absolute; top: 3px; @@ -890,7 +893,7 @@ ol.task-list li.done { z-index: 10; } #overlay-tabs { - bottom: -2px; + bottom: -1px; font-size: 1.54em; line-height: 1.54em; margin: 0; @@ -905,6 +908,9 @@ ol.task-list li.done { .overlay #content { padding: 0; } +h1#overlay-title { + font-weight: normal; +} /* Shortcut theming */ div.add-or-remove-shortcuts { @@ -973,36 +979,3 @@ div.add-or-remove-shortcuts { #user-login-form .openid-links .user-link { margin-left: 1.5em; /* LTR */ } - - -/* Available Updates */ -.update tr.ok { - color: #255b1e; - background: #e5ffe2; - border-bottom: 1px solid #89D47F; -} -.update tr.warning { - color: #840; - background: #fffce5; - border-bottom: 1px solid #ed5; -} -.update tr.warning .version-recommended { - background: #ffe; -} -.update tr.unknown { - background: #ddd; -} -.update tr.error { - color: #840; - background: #fffce5; - border-bottom: 1px solid #ed5; -} -.update tr.error .version-recommended { - background: #fdd; -} -table tbody tr.update-security, -table tbody tr.update-unsupported { - color: #840; - background: #fffce5; - border-bottom: 1px solid #ed5; -} diff --git a/themes/stark/stark.info b/themes/stark/stark.info index 39bbfe5382ea1eb48eba01c64e1ead7e04e500ba..0452cec70a098d6f50ab0072bcfb432d23cb5a3e 100644 --- a/themes/stark/stark.info +++ b/themes/stark/stark.info @@ -7,8 +7,8 @@ core = 7.x engine = phptemplate stylesheets[all][] = layout.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/themes/tests/test_theme/test_theme.info b/themes/tests/test_theme/test_theme.info index 2007d7eef2870adb35853bc1879866d179defbf1..134a34a8091b3334ea8e8b82e9c11113ce790284 100644 --- a/themes/tests/test_theme/test_theme.info +++ b/themes/tests/test_theme/test_theme.info @@ -17,8 +17,8 @@ hidden = TRUE ; file within the theme folder. stylesheets[all][] = system.base.css -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/themes/tests/update_test_basetheme/update_test_basetheme.info b/themes/tests/update_test_basetheme/update_test_basetheme.info index 175dd0ce4a175be34b204d2436c91fa38f09f8dc..3977709d006c1cfd967dc59c4806fac19bdfc87a 100644 --- a/themes/tests/update_test_basetheme/update_test_basetheme.info +++ b/themes/tests/update_test_basetheme/update_test_basetheme.info @@ -5,8 +5,8 @@ core = 7.x engine = phptemplate hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130" diff --git a/themes/tests/update_test_subtheme/update_test_subtheme.info b/themes/tests/update_test_subtheme/update_test_subtheme.info index 6203b8df55d98e03ba13b23de6af865576c636b4..2ca1e2b53d6b3662c986d8074fd5eb2a4bae4cc8 100644 --- a/themes/tests/update_test_subtheme/update_test_subtheme.info +++ b/themes/tests/update_test_subtheme/update_test_subtheme.info @@ -6,8 +6,8 @@ engine = phptemplate base theme = update_test_basetheme hidden = TRUE -; Information added by drupal.org packaging script on 2010-10-07 -version = "7.0-beta1" +; Information added by drupal.org packaging script on 2010-10-23 +version = "7.0-beta2" project = "drupal" -datestamp = "1286422862" +datestamp = "1287812130"